forked from mirrors/gecko-dev
		
	
		
			
				
	
	
		
			1249 lines
		
	
	
	
		
			41 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			1249 lines
		
	
	
	
		
			41 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/* vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ :
 | 
						|
 * This Source Code Form is subject to the terms of the Mozilla Public
 | 
						|
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 | 
						|
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 | 
						|
 | 
						|
#include "mozilla/storage.h"
 | 
						|
#include "mozilla/dom/URLSearchParams.h"
 | 
						|
#include "nsString.h"
 | 
						|
#include "nsUnicharUtils.h"
 | 
						|
#include "nsWhitespaceTokenizer.h"
 | 
						|
#include "nsEscape.h"
 | 
						|
#include "mozIPlacesAutoComplete.h"
 | 
						|
#include "SQLFunctions.h"
 | 
						|
#include "nsMathUtils.h"
 | 
						|
#include "nsUnicodeProperties.h"
 | 
						|
#include "nsUTF8Utils.h"
 | 
						|
#include "nsINavHistoryService.h"
 | 
						|
#include "nsPrintfCString.h"
 | 
						|
#include "nsNavHistory.h"
 | 
						|
#include "mozilla/Likely.h"
 | 
						|
#include "mozilla/Utf8.h"
 | 
						|
#include "nsVariant.h"
 | 
						|
 | 
						|
// Maximum number of chars to search through.
 | 
						|
// MatchAutoCompleteFunction won't look for matches over this threshold.
 | 
						|
#define MAX_CHARS_TO_SEARCH_THROUGH 255
 | 
						|
 | 
						|
using namespace mozilla::storage;
 | 
						|
 | 
						|
////////////////////////////////////////////////////////////////////////////////
 | 
						|
//// Anonymous Helpers
 | 
						|
 | 
						|
namespace {
 | 
						|
 | 
						|
typedef nsACString::const_char_iterator const_char_iterator;
 | 
						|
typedef nsACString::size_type size_type;
 | 
						|
typedef nsACString::char_type char_type;
 | 
						|
 | 
						|
/**
 | 
						|
 * Scan forward through UTF-8 text until the next potential character that
 | 
						|
 * could match a given codepoint when lower-cased (false positives are okay).
 | 
						|
 * This avoids having to actually parse the UTF-8 text, which is slow.
 | 
						|
 *
 | 
						|
 * @param aStart
 | 
						|
 *        An iterator pointing to the first character position considered.
 | 
						|
 *        It will be updated by this function.
 | 
						|
 * @param aEnd
 | 
						|
 *        An interator pointing to past-the-end of the string.
 | 
						|
 */
 | 
						|
static MOZ_ALWAYS_INLINE void goToNextSearchCandidate(
 | 
						|
    const_char_iterator& aStart, const const_char_iterator& aEnd,
 | 
						|
    uint32_t aSearchFor) {
 | 
						|
  // If the character we search for is ASCII, then we can scan until we find
 | 
						|
  // it or its ASCII uppercase character, modulo the special cases
 | 
						|
  // U+0130 LATIN CAPITAL LETTER I WITH DOT ABOVE and U+212A KELVIN SIGN
 | 
						|
  // (which are the only non-ASCII characters that lower-case to ASCII ones).
 | 
						|
  // Since false positives are okay, we approximate ASCII lower-casing by
 | 
						|
  // bit-ORing with 0x20, for increased performance.
 | 
						|
  //
 | 
						|
  // If the character we search for is *not* ASCII, we can ignore everything
 | 
						|
  // that is, since all ASCII characters lower-case to ASCII.
 | 
						|
  //
 | 
						|
  // Because of how UTF-8 uses high-order bits, this will never land us
 | 
						|
  // in the middle of a codepoint.
 | 
						|
  //
 | 
						|
  // The assumptions about Unicode made here are verified in the test_casing
 | 
						|
  // gtest.
 | 
						|
  if (aSearchFor < 128) {
 | 
						|
    // When searching for I or K, we pick out the first byte of the UTF-8
 | 
						|
    // encoding of the corresponding special case character, and look for it
 | 
						|
    // in the loop below.  For other characters we fall back to 0xff, which
 | 
						|
    // is not a valid UTF-8 byte.
 | 
						|
    unsigned char target = (unsigned char)(aSearchFor | 0x20);
 | 
						|
    unsigned char special = 0xff;
 | 
						|
    if (target == 'i' || target == 'k') {
 | 
						|
      special = (target == 'i' ? 0xc4 : 0xe2);
 | 
						|
    }
 | 
						|
 | 
						|
    while (aStart < aEnd && (unsigned char)(*aStart | 0x20) != target &&
 | 
						|
           (unsigned char)*aStart != special) {
 | 
						|
      aStart++;
 | 
						|
    }
 | 
						|
  } else {
 | 
						|
    while (aStart < aEnd && (unsigned char)(*aStart) < 128) {
 | 
						|
      aStart++;
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Check whether a character position is on a word boundary of a UTF-8 string
 | 
						|
 * (rather than within a word).  We define "within word" to be any position
 | 
						|
 * between [a-zA-Z] and [a-z] -- this lets us match CamelCase words.
 | 
						|
 * TODO: support non-latin alphabets.
 | 
						|
 *
 | 
						|
 * @param aPos
 | 
						|
 *        An iterator pointing to the character position considered.  It must
 | 
						|
 *        *not* be the first byte of a string.
 | 
						|
 *
 | 
						|
 * @return true if boundary, false otherwise.
 | 
						|
 */
 | 
						|
static MOZ_ALWAYS_INLINE bool isOnBoundary(const_char_iterator aPos) {
 | 
						|
  if ('a' <= *aPos && *aPos <= 'z') {
 | 
						|
    char prev = *(aPos - 1) | 0x20;
 | 
						|
    return !('a' <= prev && prev <= 'z');
 | 
						|
  }
 | 
						|
  return true;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Check whether a token string matches a particular position of a source
 | 
						|
 * string, case insensitively (or optionally, case and diacritic insensitively).
 | 
						|
 *
 | 
						|
 * @param aTokenStart
 | 
						|
 *        An iterator pointing to the start of the token string.
 | 
						|
 * @param aTokenEnd
 | 
						|
 *        An iterator pointing past-the-end of the token string.
 | 
						|
 * @param aSourceStart
 | 
						|
 *        An iterator pointing to the position of source string to start
 | 
						|
 *        matching at.
 | 
						|
 * @param aSourceEnd
 | 
						|
 *        An iterator pointing past-the-end of the source string.
 | 
						|
 * @param aMatchDiacritics
 | 
						|
 *        Whether or not the match is diacritic-sensitive.
 | 
						|
 *
 | 
						|
 * @return true if the string [aTokenStart, aTokenEnd) matches the start of
 | 
						|
 *         the string [aSourceStart, aSourceEnd, false otherwise.
 | 
						|
 */
 | 
						|
static MOZ_ALWAYS_INLINE bool stringMatch(const_char_iterator aTokenStart,
 | 
						|
                                          const_char_iterator aTokenEnd,
 | 
						|
                                          const_char_iterator aSourceStart,
 | 
						|
                                          const_char_iterator aSourceEnd,
 | 
						|
                                          bool aMatchDiacritics) {
 | 
						|
  const_char_iterator tokenCur = aTokenStart, sourceCur = aSourceStart;
 | 
						|
 | 
						|
  while (tokenCur < aTokenEnd) {
 | 
						|
    if (sourceCur >= aSourceEnd) {
 | 
						|
      return false;
 | 
						|
    }
 | 
						|
 | 
						|
    bool error;
 | 
						|
    if (!CaseInsensitiveUTF8CharsEqual(sourceCur, tokenCur, aSourceEnd,
 | 
						|
                                       aTokenEnd, &sourceCur, &tokenCur, &error,
 | 
						|
                                       aMatchDiacritics)) {
 | 
						|
      return false;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  return true;
 | 
						|
}
 | 
						|
 | 
						|
enum FindInStringBehavior { eFindOnBoundary, eFindAnywhere };
 | 
						|
 | 
						|
/**
 | 
						|
 * Common implementation for findAnywhere and findOnBoundary.
 | 
						|
 *
 | 
						|
 * @param aToken
 | 
						|
 *        The token we're searching for
 | 
						|
 * @param aSourceString
 | 
						|
 *        The string in which we're searching
 | 
						|
 * @param aBehavior
 | 
						|
 *        eFindOnBoundary if we should only consider matchines which occur on
 | 
						|
 *        word boundaries, or eFindAnywhere if we should consider matches
 | 
						|
 *        which appear anywhere.
 | 
						|
 *
 | 
						|
 * @return true if aToken was found in aSourceString, false otherwise.
 | 
						|
 */
 | 
						|
static bool findInString(const nsDependentCSubstring& aToken,
 | 
						|
                         const nsACString& aSourceString,
 | 
						|
                         FindInStringBehavior aBehavior) {
 | 
						|
  // GetLowerUTF8Codepoint assumes that there's at least one byte in
 | 
						|
  // the string, so don't pass an empty token here.
 | 
						|
  MOZ_ASSERT(!aToken.IsEmpty(), "Don't search for an empty token!");
 | 
						|
 | 
						|
  // We cannot match anything if there is nothing to search.
 | 
						|
  if (aSourceString.IsEmpty()) {
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
 | 
						|
  const nsNavHistory* history = nsNavHistory::GetConstHistoryService();
 | 
						|
  bool matchDiacritics = history && history->MatchDiacritics();
 | 
						|
 | 
						|
  const_char_iterator tokenStart(aToken.BeginReading()),
 | 
						|
      tokenEnd(aToken.EndReading()), tokenNext,
 | 
						|
      sourceStart(aSourceString.BeginReading()),
 | 
						|
      sourceEnd(aSourceString.EndReading()), sourceCur(sourceStart), sourceNext;
 | 
						|
 | 
						|
  uint32_t tokenFirstChar =
 | 
						|
      GetLowerUTF8Codepoint(tokenStart, tokenEnd, &tokenNext);
 | 
						|
  if (tokenFirstChar == uint32_t(-1)) {
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
  if (!matchDiacritics) {
 | 
						|
    tokenFirstChar = ToNaked(tokenFirstChar);
 | 
						|
  }
 | 
						|
 | 
						|
  for (;;) {
 | 
						|
    if (matchDiacritics) {
 | 
						|
      // Scan forward to the next viable candidate (if any).
 | 
						|
      goToNextSearchCandidate(sourceCur, sourceEnd, tokenFirstChar);
 | 
						|
    }
 | 
						|
    if (sourceCur == sourceEnd) {
 | 
						|
      break;
 | 
						|
    }
 | 
						|
 | 
						|
    // Check whether the first character in the token matches the character
 | 
						|
    // at sourceCur.  At the same time, get a pointer to the next character
 | 
						|
    // in the source.
 | 
						|
    uint32_t sourceFirstChar =
 | 
						|
        GetLowerUTF8Codepoint(sourceCur, sourceEnd, &sourceNext);
 | 
						|
    if (sourceFirstChar == uint32_t(-1)) {
 | 
						|
      return false;
 | 
						|
    }
 | 
						|
    if (!matchDiacritics) {
 | 
						|
      sourceFirstChar = ToNaked(sourceFirstChar);
 | 
						|
    }
 | 
						|
 | 
						|
    if (sourceFirstChar == tokenFirstChar &&
 | 
						|
        (aBehavior != eFindOnBoundary || sourceCur == sourceStart ||
 | 
						|
         isOnBoundary(sourceCur)) &&
 | 
						|
        stringMatch(tokenNext, tokenEnd, sourceNext, sourceEnd,
 | 
						|
                    matchDiacritics)) {
 | 
						|
      return true;
 | 
						|
    }
 | 
						|
 | 
						|
    sourceCur = sourceNext;
 | 
						|
  }
 | 
						|
 | 
						|
  return false;
 | 
						|
}
 | 
						|
 | 
						|
static MOZ_ALWAYS_INLINE nsDependentCString
 | 
						|
getSharedUTF8String(mozIStorageValueArray* aValues, uint32_t aIndex) {
 | 
						|
  uint32_t len;
 | 
						|
  const char* str = aValues->AsSharedUTF8String(aIndex, &len);
 | 
						|
  if (!str) {
 | 
						|
    return nsDependentCString("", (uint32_t)0);
 | 
						|
  }
 | 
						|
  return nsDependentCString(str, len);
 | 
						|
}
 | 
						|
 | 
						|
class MOZ_STACK_CLASS GetQueryParamIterator final
 | 
						|
    : public URLParams::ForEachIterator {
 | 
						|
 public:
 | 
						|
  explicit GetQueryParamIterator(const nsCString& aParamName,
 | 
						|
                                 nsVariant* aResult)
 | 
						|
      : mParamName(aParamName), mResult(aResult) {}
 | 
						|
 | 
						|
  bool URLParamsIterator(const nsAString& aName,
 | 
						|
                         const nsAString& aValue) override {
 | 
						|
    NS_ConvertUTF16toUTF8 name(aName);
 | 
						|
    if (!mParamName.Equals(name)) {
 | 
						|
      return true;
 | 
						|
    }
 | 
						|
    mResult->SetAsAString(aValue);
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
 | 
						|
 private:
 | 
						|
  const nsCString& mParamName;
 | 
						|
  nsVariant* mResult;
 | 
						|
};
 | 
						|
 | 
						|
/**
 | 
						|
 * Gets the length of the prefix in a URI spec.  "Prefix" is defined to be the
 | 
						|
 * scheme, colon, and, if present, two slashes.
 | 
						|
 *
 | 
						|
 * Examples:
 | 
						|
 *
 | 
						|
 *   http://example.com
 | 
						|
 *   ~~~~~~~
 | 
						|
 *   => length == 7
 | 
						|
 *
 | 
						|
 *   foo:example
 | 
						|
 *   ~~~~
 | 
						|
 *   => length == 4
 | 
						|
 *
 | 
						|
 *   not a spec
 | 
						|
 *   => length == 0
 | 
						|
 *
 | 
						|
 * @param  aSpec
 | 
						|
 *         A URI spec, or a string that may be a URI spec.
 | 
						|
 * @return The length of the prefix in the spec.  If there isn't a prefix,
 | 
						|
 *         returns 0.
 | 
						|
 */
 | 
						|
static MOZ_ALWAYS_INLINE size_type getPrefixLength(const nsACString& aSpec) {
 | 
						|
  // To keep the search bounded, look at 64 characters at most.  The longest
 | 
						|
  // IANA schemes are ~30, so double that and round up to a nice number.
 | 
						|
  size_type length = std::min(static_cast<size_type>(64), aSpec.Length());
 | 
						|
  for (size_type i = 0; i < length; ++i) {
 | 
						|
    if (aSpec[i] == static_cast<char_type>(':')) {
 | 
						|
      // Found the ':'.  Now skip past "//", if present.
 | 
						|
      if (i + 2 < aSpec.Length() &&
 | 
						|
          aSpec[i + 1] == static_cast<char_type>('/') &&
 | 
						|
          aSpec[i + 2] == static_cast<char_type>('/')) {
 | 
						|
        i += 2;
 | 
						|
      }
 | 
						|
      return i + 1;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Gets the index in a URI spec of the host and port substring and optionally
 | 
						|
 * its length.
 | 
						|
 *
 | 
						|
 * Examples:
 | 
						|
 *
 | 
						|
 *   http://example.com/
 | 
						|
 *          ~~~~~~~~~~~
 | 
						|
 *   => index == 7, length == 11
 | 
						|
 *
 | 
						|
 *   http://example.com:8888/
 | 
						|
 *          ~~~~~~~~~~~~~~~~
 | 
						|
 *   => index == 7, length == 16
 | 
						|
 *
 | 
						|
 *   http://user:pass@example.com/
 | 
						|
 *                    ~~~~~~~~~~~
 | 
						|
 *   => index == 17, length == 11
 | 
						|
 *
 | 
						|
 *   foo:example
 | 
						|
 *       ~~~~~~~
 | 
						|
 *   => index == 4, length == 7
 | 
						|
 *
 | 
						|
 *   not a spec
 | 
						|
 *   ~~~~~~~~~~
 | 
						|
 *   => index == 0, length == 10
 | 
						|
 *
 | 
						|
 * @param  aSpec
 | 
						|
 *         A URI spec, or a string that may be a URI spec.
 | 
						|
 * @param  _hostAndPortLength
 | 
						|
 *         The length of the host and port substring is returned through this
 | 
						|
 *         param.  Pass null if you don't care.
 | 
						|
 * @return The length of the host and port substring in the spec.  If aSpec
 | 
						|
 *         doesn't look like a URI, then the entire aSpec is assumed to be a
 | 
						|
 *         "host and port", and this returns 0, and _hostAndPortLength will be
 | 
						|
 *         the length of aSpec.
 | 
						|
 */
 | 
						|
static MOZ_ALWAYS_INLINE size_type
 | 
						|
indexOfHostAndPort(const nsACString& aSpec, size_type* _hostAndPortLength) {
 | 
						|
  size_type index = getPrefixLength(aSpec);
 | 
						|
  size_type i = index;
 | 
						|
  for (; i < aSpec.Length(); ++i) {
 | 
						|
    // RFC 3986 (URIs): The origin ("authority") is terminated by '/', '?', or
 | 
						|
    // '#' (or the end of the URI).
 | 
						|
    if (aSpec[i] == static_cast<char_type>('/') ||
 | 
						|
        aSpec[i] == static_cast<char_type>('?') ||
 | 
						|
        aSpec[i] == static_cast<char_type>('#')) {
 | 
						|
      break;
 | 
						|
    }
 | 
						|
    // RFC 3986: '@' marks the end of the userinfo component.
 | 
						|
    if (aSpec[i] == static_cast<char_type>('@')) {
 | 
						|
      index = i + 1;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  if (_hostAndPortLength) {
 | 
						|
    *_hostAndPortLength = i - index;
 | 
						|
  }
 | 
						|
  return index;
 | 
						|
}
 | 
						|
 | 
						|
}  // End anonymous namespace
 | 
						|
 | 
						|
namespace mozilla {
 | 
						|
namespace places {
 | 
						|
 | 
						|
////////////////////////////////////////////////////////////////////////////////
 | 
						|
//// AutoComplete Matching Function
 | 
						|
 | 
						|
/* static */
 | 
						|
nsresult MatchAutoCompleteFunction::create(mozIStorageConnection* aDBConn) {
 | 
						|
  RefPtr<MatchAutoCompleteFunction> function = new MatchAutoCompleteFunction();
 | 
						|
 | 
						|
  nsresult rv = aDBConn->CreateFunction("autocomplete_match"_ns,
 | 
						|
                                        kArgIndexLength, function);
 | 
						|
  NS_ENSURE_SUCCESS(rv, rv);
 | 
						|
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
/* static */
 | 
						|
nsDependentCSubstring MatchAutoCompleteFunction::fixupURISpec(
 | 
						|
    const nsACString& aURISpec, int32_t aMatchBehavior, nsACString& aSpecBuf) {
 | 
						|
  nsDependentCSubstring fixedSpec;
 | 
						|
 | 
						|
  // Try to unescape the string.  If that succeeds and yields a different
 | 
						|
  // string which is also valid UTF-8, we'll use it.
 | 
						|
  // Otherwise, we will simply use our original string.
 | 
						|
  bool unescaped = NS_UnescapeURL(aURISpec.BeginReading(), aURISpec.Length(),
 | 
						|
                                  esc_SkipControl, aSpecBuf);
 | 
						|
  if (unescaped && IsUtf8(aSpecBuf)) {
 | 
						|
    fixedSpec.Rebind(aSpecBuf, 0);
 | 
						|
  } else {
 | 
						|
    fixedSpec.Rebind(aURISpec, 0);
 | 
						|
  }
 | 
						|
 | 
						|
  if (aMatchBehavior == mozIPlacesAutoComplete::MATCH_ANYWHERE_UNMODIFIED)
 | 
						|
    return fixedSpec;
 | 
						|
 | 
						|
  if (StringBeginsWith(fixedSpec, "http://"_ns)) {
 | 
						|
    fixedSpec.Rebind(fixedSpec, 7);
 | 
						|
  } else if (StringBeginsWith(fixedSpec, "https://"_ns)) {
 | 
						|
    fixedSpec.Rebind(fixedSpec, 8);
 | 
						|
  } else if (StringBeginsWith(fixedSpec, "ftp://"_ns)) {
 | 
						|
    fixedSpec.Rebind(fixedSpec, 6);
 | 
						|
  }
 | 
						|
 | 
						|
  return fixedSpec;
 | 
						|
}
 | 
						|
 | 
						|
/* static */
 | 
						|
bool MatchAutoCompleteFunction::findAnywhere(
 | 
						|
    const nsDependentCSubstring& aToken, const nsACString& aSourceString) {
 | 
						|
  // We can't use FindInReadable here; it works only for ASCII.
 | 
						|
 | 
						|
  return findInString(aToken, aSourceString, eFindAnywhere);
 | 
						|
}
 | 
						|
 | 
						|
/* static */
 | 
						|
bool MatchAutoCompleteFunction::findOnBoundary(
 | 
						|
    const nsDependentCSubstring& aToken, const nsACString& aSourceString) {
 | 
						|
  return findInString(aToken, aSourceString, eFindOnBoundary);
 | 
						|
}
 | 
						|
 | 
						|
/* static */
 | 
						|
MatchAutoCompleteFunction::searchFunctionPtr
 | 
						|
MatchAutoCompleteFunction::getSearchFunction(int32_t aBehavior) {
 | 
						|
  switch (aBehavior) {
 | 
						|
    case mozIPlacesAutoComplete::MATCH_ANYWHERE:
 | 
						|
    case mozIPlacesAutoComplete::MATCH_ANYWHERE_UNMODIFIED:
 | 
						|
      return findAnywhere;
 | 
						|
    case mozIPlacesAutoComplete::MATCH_BOUNDARY:
 | 
						|
    default:
 | 
						|
      return findOnBoundary;
 | 
						|
  };
 | 
						|
}
 | 
						|
 | 
						|
NS_IMPL_ISUPPORTS(MatchAutoCompleteFunction, mozIStorageFunction)
 | 
						|
 | 
						|
MatchAutoCompleteFunction::MatchAutoCompleteFunction()
 | 
						|
    : mCachedZero(new IntegerVariant(0)), mCachedOne(new IntegerVariant(1)) {
 | 
						|
  static_assert(IntegerVariant::HasThreadSafeRefCnt::value,
 | 
						|
                "Caching assumes that variants have thread-safe refcounting");
 | 
						|
}
 | 
						|
 | 
						|
NS_IMETHODIMP
 | 
						|
MatchAutoCompleteFunction::OnFunctionCall(mozIStorageValueArray* aArguments,
 | 
						|
                                          nsIVariant** _result) {
 | 
						|
  // Macro to make the code a bit cleaner and easier to read.  Operates on
 | 
						|
  // searchBehavior.
 | 
						|
  int32_t searchBehavior = aArguments->AsInt32(kArgIndexSearchBehavior);
 | 
						|
#define HAS_BEHAVIOR(aBitName) \
 | 
						|
  (searchBehavior & mozIPlacesAutoComplete::BEHAVIOR_##aBitName)
 | 
						|
 | 
						|
  nsDependentCString searchString =
 | 
						|
      getSharedUTF8String(aArguments, kArgSearchString);
 | 
						|
  nsDependentCString url = getSharedUTF8String(aArguments, kArgIndexURL);
 | 
						|
 | 
						|
  int32_t matchBehavior = aArguments->AsInt32(kArgIndexMatchBehavior);
 | 
						|
 | 
						|
  // We only want to filter javascript: URLs if we are not supposed to search
 | 
						|
  // for them, and the search does not start with "javascript:".
 | 
						|
  if (matchBehavior != mozIPlacesAutoComplete::MATCH_ANYWHERE_UNMODIFIED &&
 | 
						|
      StringBeginsWith(url, "javascript:"_ns) && !HAS_BEHAVIOR(JAVASCRIPT) &&
 | 
						|
      !StringBeginsWith(searchString, "javascript:"_ns)) {
 | 
						|
    NS_ADDREF(*_result = mCachedZero);
 | 
						|
    return NS_OK;
 | 
						|
  }
 | 
						|
 | 
						|
  int32_t visitCount = aArguments->AsInt32(kArgIndexVisitCount);
 | 
						|
  // Filtering on typed is no more used by Firefox, it is still being used by
 | 
						|
  // comm-central clients.
 | 
						|
  bool typed = aArguments->AsInt32(kArgIndexTyped) ? true : false;
 | 
						|
  bool bookmark = aArguments->AsInt32(kArgIndexBookmark) ? true : false;
 | 
						|
  nsDependentCString tags = getSharedUTF8String(aArguments, kArgIndexTags);
 | 
						|
  int32_t openPageCount = aArguments->AsInt32(kArgIndexOpenPageCount);
 | 
						|
  bool matches = false;
 | 
						|
  if (HAS_BEHAVIOR(RESTRICT)) {
 | 
						|
    // Make sure we match all the filter requirements.  If a given restriction
 | 
						|
    // is active, make sure the corresponding condition is not true.
 | 
						|
    matches = (!HAS_BEHAVIOR(HISTORY) || visitCount > 0) &&
 | 
						|
              (!HAS_BEHAVIOR(TYPED) || typed) &&
 | 
						|
              (!HAS_BEHAVIOR(BOOKMARK) || bookmark) &&
 | 
						|
              (!HAS_BEHAVIOR(TAG) || !tags.IsVoid()) &&
 | 
						|
              (!HAS_BEHAVIOR(OPENPAGE) || openPageCount > 0);
 | 
						|
  } else {
 | 
						|
    // Make sure that we match all the filter requirements and that the
 | 
						|
    // corresponding condition is true if at least a given restriction is
 | 
						|
    // active.
 | 
						|
    matches = (HAS_BEHAVIOR(HISTORY) && visitCount > 0) ||
 | 
						|
              (HAS_BEHAVIOR(TYPED) && typed) ||
 | 
						|
              (HAS_BEHAVIOR(BOOKMARK) && bookmark) ||
 | 
						|
              (HAS_BEHAVIOR(TAG) && !tags.IsVoid()) ||
 | 
						|
              (HAS_BEHAVIOR(OPENPAGE) && openPageCount > 0);
 | 
						|
  }
 | 
						|
 | 
						|
  if (!matches) {
 | 
						|
    NS_ADDREF(*_result = mCachedZero);
 | 
						|
    return NS_OK;
 | 
						|
  }
 | 
						|
 | 
						|
  // Obtain our search function.
 | 
						|
  searchFunctionPtr searchFunction = getSearchFunction(matchBehavior);
 | 
						|
 | 
						|
  // Clean up our URI spec and prepare it for searching.
 | 
						|
  nsCString fixedUrlBuf;
 | 
						|
  nsDependentCSubstring fixedUrl =
 | 
						|
      fixupURISpec(url, matchBehavior, fixedUrlBuf);
 | 
						|
  // Limit the number of chars we search through.
 | 
						|
  const nsDependentCSubstring& trimmedUrl =
 | 
						|
      Substring(fixedUrl, 0, MAX_CHARS_TO_SEARCH_THROUGH);
 | 
						|
 | 
						|
  nsDependentCString title = getSharedUTF8String(aArguments, kArgIndexTitle);
 | 
						|
  // Limit the number of chars we search through.
 | 
						|
  const nsDependentCSubstring& trimmedTitle =
 | 
						|
      Substring(title, 0, MAX_CHARS_TO_SEARCH_THROUGH);
 | 
						|
 | 
						|
  // Determine if every token matches either the bookmark title, tags, page
 | 
						|
  // title, or page URL.
 | 
						|
  nsCWhitespaceTokenizer tokenizer(searchString);
 | 
						|
  while (matches && tokenizer.hasMoreTokens()) {
 | 
						|
    const nsDependentCSubstring& token = tokenizer.nextToken();
 | 
						|
 | 
						|
    if (HAS_BEHAVIOR(TITLE) && HAS_BEHAVIOR(URL)) {
 | 
						|
      matches = (searchFunction(token, trimmedTitle) ||
 | 
						|
                 searchFunction(token, tags)) &&
 | 
						|
                searchFunction(token, trimmedUrl);
 | 
						|
    } else if (HAS_BEHAVIOR(TITLE)) {
 | 
						|
      matches =
 | 
						|
          searchFunction(token, trimmedTitle) || searchFunction(token, tags);
 | 
						|
    } else if (HAS_BEHAVIOR(URL)) {
 | 
						|
      matches = searchFunction(token, trimmedUrl);
 | 
						|
    } else {
 | 
						|
      matches = searchFunction(token, trimmedTitle) ||
 | 
						|
                searchFunction(token, tags) ||
 | 
						|
                searchFunction(token, trimmedUrl);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  NS_ADDREF(*_result = (matches ? mCachedOne : mCachedZero));
 | 
						|
  return NS_OK;
 | 
						|
#undef HAS_BEHAVIOR
 | 
						|
}
 | 
						|
 | 
						|
////////////////////////////////////////////////////////////////////////////////
 | 
						|
//// Frecency Calculation Function
 | 
						|
 | 
						|
/* static */
 | 
						|
nsresult CalculateFrecencyFunction::create(mozIStorageConnection* aDBConn) {
 | 
						|
  RefPtr<CalculateFrecencyFunction> function = new CalculateFrecencyFunction();
 | 
						|
 | 
						|
  nsresult rv = aDBConn->CreateFunction("calculate_frecency"_ns, -1, function);
 | 
						|
  NS_ENSURE_SUCCESS(rv, rv);
 | 
						|
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
NS_IMPL_ISUPPORTS(CalculateFrecencyFunction, mozIStorageFunction)
 | 
						|
 | 
						|
NS_IMETHODIMP
 | 
						|
CalculateFrecencyFunction::OnFunctionCall(mozIStorageValueArray* aArguments,
 | 
						|
                                          nsIVariant** _result) {
 | 
						|
  // Fetch arguments.  Use default values if they were omitted.
 | 
						|
  uint32_t numEntries;
 | 
						|
  nsresult rv = aArguments->GetNumEntries(&numEntries);
 | 
						|
  NS_ENSURE_SUCCESS(rv, rv);
 | 
						|
  MOZ_ASSERT(numEntries <= 2, "unexpected number of arguments");
 | 
						|
 | 
						|
  int64_t pageId = aArguments->AsInt64(0);
 | 
						|
  MOZ_ASSERT(pageId > 0, "Should always pass a valid page id");
 | 
						|
  if (pageId <= 0) {
 | 
						|
    NS_ADDREF(*_result = new IntegerVariant(0));
 | 
						|
    return NS_OK;
 | 
						|
  }
 | 
						|
 | 
						|
  enum RedirectBonus { eUnknown, eRedirect, eNormal };
 | 
						|
 | 
						|
  RedirectBonus mostRecentVisitBonus = eUnknown;
 | 
						|
 | 
						|
  if (numEntries > 1) {
 | 
						|
    mostRecentVisitBonus = aArguments->AsInt32(1) ? eRedirect : eNormal;
 | 
						|
  }
 | 
						|
 | 
						|
  int32_t typed = 0;
 | 
						|
  int32_t visitCount = 0;
 | 
						|
  bool hasBookmark = false;
 | 
						|
  int32_t isQuery = 0;
 | 
						|
  float pointsForSampledVisits = 0.0;
 | 
						|
  int32_t numSampledVisits = 0;
 | 
						|
  int32_t bonus = 0;
 | 
						|
 | 
						|
  // This is a const version of the history object for thread-safety.
 | 
						|
  const nsNavHistory* history = nsNavHistory::GetConstHistoryService();
 | 
						|
  NS_ENSURE_STATE(history);
 | 
						|
  RefPtr<Database> DB = Database::GetDatabase();
 | 
						|
  NS_ENSURE_STATE(DB);
 | 
						|
 | 
						|
  // Fetch the page stats from the database.
 | 
						|
  {
 | 
						|
    nsCOMPtr<mozIStorageStatement> getPageInfo = DB->GetStatement(
 | 
						|
        "SELECT typed, visit_count, foreign_count, "
 | 
						|
        "(substr(url, 0, 7) = 'place:') "
 | 
						|
        "FROM moz_places "
 | 
						|
        "WHERE id = :page_id ");
 | 
						|
    NS_ENSURE_STATE(getPageInfo);
 | 
						|
    mozStorageStatementScoper infoScoper(getPageInfo);
 | 
						|
 | 
						|
    rv = getPageInfo->BindInt64ByName("page_id"_ns, pageId);
 | 
						|
    NS_ENSURE_SUCCESS(rv, rv);
 | 
						|
 | 
						|
    bool hasResult = false;
 | 
						|
    rv = getPageInfo->ExecuteStep(&hasResult);
 | 
						|
    NS_ENSURE_TRUE(NS_SUCCEEDED(rv) && hasResult, NS_ERROR_UNEXPECTED);
 | 
						|
 | 
						|
    rv = getPageInfo->GetInt32(0, &typed);
 | 
						|
    NS_ENSURE_SUCCESS(rv, rv);
 | 
						|
    rv = getPageInfo->GetInt32(1, &visitCount);
 | 
						|
    NS_ENSURE_SUCCESS(rv, rv);
 | 
						|
    int32_t foreignCount = 0;
 | 
						|
    rv = getPageInfo->GetInt32(2, &foreignCount);
 | 
						|
    NS_ENSURE_SUCCESS(rv, rv);
 | 
						|
    hasBookmark = foreignCount > 0;
 | 
						|
    rv = getPageInfo->GetInt32(3, &isQuery);
 | 
						|
    NS_ENSURE_SUCCESS(rv, rv);
 | 
						|
  }
 | 
						|
 | 
						|
  if (visitCount > 0) {
 | 
						|
    // Get a sample of the last visits to the page, to calculate its weight.
 | 
						|
    // In case the visit is a redirect target, calculate the frecency
 | 
						|
    // as if the original page was visited.
 | 
						|
    // If it's a redirect source, we may want to use a lower bonus.
 | 
						|
    nsCString redirectsTransitionFragment = nsPrintfCString(
 | 
						|
        "%d AND %d ", nsINavHistoryService::TRANSITION_REDIRECT_PERMANENT,
 | 
						|
        nsINavHistoryService::TRANSITION_REDIRECT_TEMPORARY);
 | 
						|
    nsCOMPtr<mozIStorageStatement> getVisits = DB->GetStatement(
 | 
						|
        nsLiteralCString(
 | 
						|
            "/* do not warn (bug 659740 - SQLite may ignore index if few "
 | 
						|
            "visits exist) */"
 | 
						|
            "SELECT "
 | 
						|
            "IFNULL(origin.visit_type, v.visit_type) AS visit_type, "
 | 
						|
            "target.visit_type AS target_visit_type, "
 | 
						|
            "ROUND((strftime('%s','now','localtime','utc') - "
 | 
						|
            "v.visit_date/1000000)/86400) AS age_in_days "
 | 
						|
            "FROM moz_historyvisits v "
 | 
						|
            "LEFT JOIN moz_historyvisits origin ON origin.id = v.from_visit "
 | 
						|
            "AND v.visit_type BETWEEN ") +
 | 
						|
        redirectsTransitionFragment +
 | 
						|
        nsLiteralCString(
 | 
						|
            "LEFT JOIN moz_historyvisits target ON v.id = target.from_visit "
 | 
						|
            "AND target.visit_type BETWEEN ") +
 | 
						|
        redirectsTransitionFragment +
 | 
						|
        nsLiteralCString("WHERE v.place_id = :page_id "
 | 
						|
                         "ORDER BY v.visit_date DESC "
 | 
						|
                         "LIMIT :max_visits "));
 | 
						|
    NS_ENSURE_STATE(getVisits);
 | 
						|
    mozStorageStatementScoper visitsScoper(getVisits);
 | 
						|
    rv = getVisits->BindInt64ByName("page_id"_ns, pageId);
 | 
						|
    NS_ENSURE_SUCCESS(rv, rv);
 | 
						|
    rv = getVisits->BindInt32ByName("max_visits"_ns,
 | 
						|
                                    history->GetNumVisitsForFrecency());
 | 
						|
    NS_ENSURE_SUCCESS(rv, rv);
 | 
						|
 | 
						|
    // Fetch only a limited number of recent visits.
 | 
						|
    bool hasResult = false;
 | 
						|
    while (NS_SUCCEEDED(getVisits->ExecuteStep(&hasResult)) && hasResult) {
 | 
						|
      // If this is a redirect target, we'll use the visitType of the source,
 | 
						|
      // otherwise the actual visitType.
 | 
						|
      int32_t visitType = getVisits->AsInt32(0);
 | 
						|
 | 
						|
      // When adding a new visit, we should haved passed-in whether we should
 | 
						|
      // use the redirect bonus. We can't fetch this information from the
 | 
						|
      // database, because we only store redirect targets.
 | 
						|
      // For older visits we extract the value from the database.
 | 
						|
      bool useRedirectBonus = mostRecentVisitBonus == eRedirect;
 | 
						|
      if (mostRecentVisitBonus == eUnknown || numSampledVisits > 0) {
 | 
						|
        int32_t targetVisitType = getVisits->AsInt32(1);
 | 
						|
        useRedirectBonus =
 | 
						|
            targetVisitType ==
 | 
						|
                nsINavHistoryService::TRANSITION_REDIRECT_PERMANENT ||
 | 
						|
            (targetVisitType ==
 | 
						|
                 nsINavHistoryService::TRANSITION_REDIRECT_TEMPORARY &&
 | 
						|
             visitType != nsINavHistoryService::TRANSITION_TYPED);
 | 
						|
      }
 | 
						|
 | 
						|
      bonus = history->GetFrecencyTransitionBonus(visitType, true,
 | 
						|
                                                  useRedirectBonus);
 | 
						|
 | 
						|
      // Add the bookmark visit bonus.
 | 
						|
      if (hasBookmark) {
 | 
						|
        bonus += history->GetFrecencyTransitionBonus(
 | 
						|
            nsINavHistoryService::TRANSITION_BOOKMARK, true);
 | 
						|
      }
 | 
						|
 | 
						|
      // If bonus was zero, we can skip the work to determine the weight.
 | 
						|
      if (bonus) {
 | 
						|
        int32_t ageInDays = getVisits->AsInt32(2);
 | 
						|
        int32_t weight = history->GetFrecencyAgedWeight(ageInDays);
 | 
						|
        pointsForSampledVisits += (float)(weight * (bonus / 100.0));
 | 
						|
      }
 | 
						|
 | 
						|
      numSampledVisits++;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  // If we sampled some visits for this page, use the calculated weight.
 | 
						|
  if (numSampledVisits) {
 | 
						|
    // We were unable to calculate points, maybe cause all the visits in the
 | 
						|
    // sample had a zero bonus. Though, we know the page has some past valid
 | 
						|
    // visit, or visit_count would be zero. Thus we set the frecency to
 | 
						|
    // -1, so they are still shown in autocomplete.
 | 
						|
    if (!pointsForSampledVisits) {
 | 
						|
      NS_ADDREF(*_result = new IntegerVariant(-1));
 | 
						|
    } else {
 | 
						|
      // Estimate frecency using the sampled visits.
 | 
						|
      // Use ceilf() so that we don't round down to 0, which
 | 
						|
      // would cause us to completely ignore the place during autocomplete.
 | 
						|
      NS_ADDREF(*_result = new IntegerVariant(
 | 
						|
                    (int32_t)ceilf(visitCount * ceilf(pointsForSampledVisits) /
 | 
						|
                                   numSampledVisits)));
 | 
						|
    }
 | 
						|
    return NS_OK;
 | 
						|
  }
 | 
						|
 | 
						|
  // Otherwise this page has no visits, it may be bookmarked.
 | 
						|
  if (!hasBookmark || isQuery) {
 | 
						|
    NS_ADDREF(*_result = new IntegerVariant(0));
 | 
						|
    return NS_OK;
 | 
						|
  }
 | 
						|
 | 
						|
  // For unvisited bookmarks, produce a non-zero frecency, so that they show
 | 
						|
  // up in URL bar autocomplete.
 | 
						|
  visitCount = 1;
 | 
						|
 | 
						|
  // Make it so something bookmarked and typed will have a higher frecency
 | 
						|
  // than something just typed or just bookmarked.
 | 
						|
  bonus += history->GetFrecencyTransitionBonus(
 | 
						|
      nsINavHistoryService::TRANSITION_BOOKMARK, false);
 | 
						|
  if (typed) {
 | 
						|
    bonus += history->GetFrecencyTransitionBonus(
 | 
						|
        nsINavHistoryService::TRANSITION_TYPED, false);
 | 
						|
  }
 | 
						|
 | 
						|
  // Assume "now" as our ageInDays, so use the first bucket.
 | 
						|
  pointsForSampledVisits =
 | 
						|
      history->GetFrecencyBucketWeight(1) * (bonus / (float)100.0);
 | 
						|
 | 
						|
  // use ceilf() so that we don't round down to 0, which
 | 
						|
  // would cause us to completely ignore the place during autocomplete
 | 
						|
  NS_ADDREF(*_result = new IntegerVariant(
 | 
						|
                (int32_t)ceilf(visitCount * ceilf(pointsForSampledVisits))));
 | 
						|
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
////////////////////////////////////////////////////////////////////////////////
 | 
						|
//// GUID Creation Function
 | 
						|
 | 
						|
/* static */
 | 
						|
nsresult GenerateGUIDFunction::create(mozIStorageConnection* aDBConn) {
 | 
						|
  RefPtr<GenerateGUIDFunction> function = new GenerateGUIDFunction();
 | 
						|
  nsresult rv = aDBConn->CreateFunction("generate_guid"_ns, 0, function);
 | 
						|
  NS_ENSURE_SUCCESS(rv, rv);
 | 
						|
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
NS_IMPL_ISUPPORTS(GenerateGUIDFunction, mozIStorageFunction)
 | 
						|
 | 
						|
NS_IMETHODIMP
 | 
						|
GenerateGUIDFunction::OnFunctionCall(mozIStorageValueArray* aArguments,
 | 
						|
                                     nsIVariant** _result) {
 | 
						|
  nsAutoCString guid;
 | 
						|
  nsresult rv = GenerateGUID(guid);
 | 
						|
  NS_ENSURE_SUCCESS(rv, rv);
 | 
						|
 | 
						|
  NS_ADDREF(*_result = new UTF8TextVariant(guid));
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
////////////////////////////////////////////////////////////////////////////////
 | 
						|
//// GUID Validation Function
 | 
						|
 | 
						|
/* static */
 | 
						|
nsresult IsValidGUIDFunction::create(mozIStorageConnection* aDBConn) {
 | 
						|
  RefPtr<IsValidGUIDFunction> function = new IsValidGUIDFunction();
 | 
						|
  return aDBConn->CreateFunction("is_valid_guid"_ns, 1, function);
 | 
						|
}
 | 
						|
 | 
						|
NS_IMPL_ISUPPORTS(IsValidGUIDFunction, mozIStorageFunction)
 | 
						|
 | 
						|
NS_IMETHODIMP
 | 
						|
IsValidGUIDFunction::OnFunctionCall(mozIStorageValueArray* aArguments,
 | 
						|
                                    nsIVariant** _result) {
 | 
						|
  // Must have non-null function arguments.
 | 
						|
  MOZ_ASSERT(aArguments);
 | 
						|
 | 
						|
  nsAutoCString guid;
 | 
						|
  aArguments->GetUTF8String(0, guid);
 | 
						|
 | 
						|
  RefPtr<nsVariant> result = new nsVariant();
 | 
						|
  result->SetAsBool(IsValidGUID(guid));
 | 
						|
  result.forget(_result);
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
////////////////////////////////////////////////////////////////////////////////
 | 
						|
//// Get Unreversed Host Function
 | 
						|
 | 
						|
/* static */
 | 
						|
nsresult GetUnreversedHostFunction::create(mozIStorageConnection* aDBConn) {
 | 
						|
  RefPtr<GetUnreversedHostFunction> function = new GetUnreversedHostFunction();
 | 
						|
  nsresult rv = aDBConn->CreateFunction("get_unreversed_host"_ns, 1, function);
 | 
						|
  NS_ENSURE_SUCCESS(rv, rv);
 | 
						|
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
NS_IMPL_ISUPPORTS(GetUnreversedHostFunction, mozIStorageFunction)
 | 
						|
 | 
						|
NS_IMETHODIMP
 | 
						|
GetUnreversedHostFunction::OnFunctionCall(mozIStorageValueArray* aArguments,
 | 
						|
                                          nsIVariant** _result) {
 | 
						|
  // Must have non-null function arguments.
 | 
						|
  MOZ_ASSERT(aArguments);
 | 
						|
 | 
						|
  nsAutoString src;
 | 
						|
  aArguments->GetString(0, src);
 | 
						|
 | 
						|
  RefPtr<nsVariant> result = new nsVariant();
 | 
						|
 | 
						|
  if (src.Length() > 1) {
 | 
						|
    src.Truncate(src.Length() - 1);
 | 
						|
    nsAutoString dest;
 | 
						|
    ReverseString(src, dest);
 | 
						|
    result->SetAsAString(dest);
 | 
						|
  } else {
 | 
						|
    result->SetAsAString(EmptyString());
 | 
						|
  }
 | 
						|
  result.forget(_result);
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
////////////////////////////////////////////////////////////////////////////////
 | 
						|
//// Fixup URL Function
 | 
						|
 | 
						|
/* static */
 | 
						|
nsresult FixupURLFunction::create(mozIStorageConnection* aDBConn) {
 | 
						|
  RefPtr<FixupURLFunction> function = new FixupURLFunction();
 | 
						|
  nsresult rv = aDBConn->CreateFunction("fixup_url"_ns, 1, function);
 | 
						|
  NS_ENSURE_SUCCESS(rv, rv);
 | 
						|
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
NS_IMPL_ISUPPORTS(FixupURLFunction, mozIStorageFunction)
 | 
						|
 | 
						|
NS_IMETHODIMP
 | 
						|
FixupURLFunction::OnFunctionCall(mozIStorageValueArray* aArguments,
 | 
						|
                                 nsIVariant** _result) {
 | 
						|
  // Must have non-null function arguments.
 | 
						|
  MOZ_ASSERT(aArguments);
 | 
						|
 | 
						|
  nsAutoString src;
 | 
						|
  aArguments->GetString(0, src);
 | 
						|
 | 
						|
  RefPtr<nsVariant> result = new nsVariant();
 | 
						|
 | 
						|
  if (StringBeginsWith(src, u"http://"_ns))
 | 
						|
    src.Cut(0, 7);
 | 
						|
  else if (StringBeginsWith(src, u"https://"_ns))
 | 
						|
    src.Cut(0, 8);
 | 
						|
  else if (StringBeginsWith(src, u"ftp://"_ns))
 | 
						|
    src.Cut(0, 6);
 | 
						|
 | 
						|
  // Remove common URL hostname prefixes
 | 
						|
  if (StringBeginsWith(src, u"www."_ns)) {
 | 
						|
    src.Cut(0, 4);
 | 
						|
  }
 | 
						|
 | 
						|
  result->SetAsAString(src);
 | 
						|
  result.forget(_result);
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
////////////////////////////////////////////////////////////////////////////////
 | 
						|
//// Frecency Changed Notification Function
 | 
						|
 | 
						|
/* static */
 | 
						|
nsresult FrecencyNotificationFunction::create(mozIStorageConnection* aDBConn) {
 | 
						|
  RefPtr<FrecencyNotificationFunction> function =
 | 
						|
      new FrecencyNotificationFunction();
 | 
						|
  nsresult rv = aDBConn->CreateFunction("notify_frecency"_ns, 5, function);
 | 
						|
  NS_ENSURE_SUCCESS(rv, rv);
 | 
						|
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
NS_IMPL_ISUPPORTS(FrecencyNotificationFunction, mozIStorageFunction)
 | 
						|
 | 
						|
NS_IMETHODIMP
 | 
						|
FrecencyNotificationFunction::OnFunctionCall(mozIStorageValueArray* aArgs,
 | 
						|
                                             nsIVariant** _result) {
 | 
						|
  uint32_t numArgs;
 | 
						|
  nsresult rv = aArgs->GetNumEntries(&numArgs);
 | 
						|
  NS_ENSURE_SUCCESS(rv, rv);
 | 
						|
  MOZ_ASSERT(numArgs == 5);
 | 
						|
 | 
						|
  int32_t newFrecency = aArgs->AsInt32(0);
 | 
						|
 | 
						|
  nsAutoCString spec;
 | 
						|
  rv = aArgs->GetUTF8String(1, spec);
 | 
						|
  NS_ENSURE_SUCCESS(rv, rv);
 | 
						|
 | 
						|
  nsAutoCString guid;
 | 
						|
  rv = aArgs->GetUTF8String(2, guid);
 | 
						|
  NS_ENSURE_SUCCESS(rv, rv);
 | 
						|
 | 
						|
  bool hidden = static_cast<bool>(aArgs->AsInt32(3));
 | 
						|
  PRTime lastVisitDate = static_cast<PRTime>(aArgs->AsInt64(4));
 | 
						|
 | 
						|
  const nsNavHistory* navHistory = nsNavHistory::GetConstHistoryService();
 | 
						|
  NS_ENSURE_STATE(navHistory);
 | 
						|
  navHistory->DispatchFrecencyChangedNotification(spec, newFrecency, guid,
 | 
						|
                                                  hidden, lastVisitDate);
 | 
						|
 | 
						|
  RefPtr<nsVariant> result = new nsVariant();
 | 
						|
  rv = result->SetAsInt32(newFrecency);
 | 
						|
  NS_ENSURE_SUCCESS(rv, rv);
 | 
						|
  result.forget(_result);
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
////////////////////////////////////////////////////////////////////////////////
 | 
						|
//// Store Last Inserted Id Function
 | 
						|
 | 
						|
/* static */
 | 
						|
nsresult StoreLastInsertedIdFunction::create(mozIStorageConnection* aDBConn) {
 | 
						|
  RefPtr<StoreLastInsertedIdFunction> function =
 | 
						|
      new StoreLastInsertedIdFunction();
 | 
						|
  nsresult rv =
 | 
						|
      aDBConn->CreateFunction("store_last_inserted_id"_ns, 2, function);
 | 
						|
  NS_ENSURE_SUCCESS(rv, rv);
 | 
						|
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
NS_IMPL_ISUPPORTS(StoreLastInsertedIdFunction, mozIStorageFunction)
 | 
						|
 | 
						|
NS_IMETHODIMP
 | 
						|
StoreLastInsertedIdFunction::OnFunctionCall(mozIStorageValueArray* aArgs,
 | 
						|
                                            nsIVariant** _result) {
 | 
						|
  uint32_t numArgs;
 | 
						|
  nsresult rv = aArgs->GetNumEntries(&numArgs);
 | 
						|
  NS_ENSURE_SUCCESS(rv, rv);
 | 
						|
  MOZ_ASSERT(numArgs == 2);
 | 
						|
 | 
						|
  nsAutoCString table;
 | 
						|
  rv = aArgs->GetUTF8String(0, table);
 | 
						|
  NS_ENSURE_SUCCESS(rv, rv);
 | 
						|
 | 
						|
  int64_t lastInsertedId = aArgs->AsInt64(1);
 | 
						|
 | 
						|
  MOZ_ASSERT(table.EqualsLiteral("moz_places") ||
 | 
						|
             table.EqualsLiteral("moz_historyvisits") ||
 | 
						|
             table.EqualsLiteral("moz_bookmarks") ||
 | 
						|
             table.EqualsLiteral("moz_icons"));
 | 
						|
 | 
						|
  if (table.EqualsLiteral("moz_bookmarks")) {
 | 
						|
    nsNavBookmarks::StoreLastInsertedId(table, lastInsertedId);
 | 
						|
  } else if (table.EqualsLiteral("moz_icons")) {
 | 
						|
    nsFaviconService::StoreLastInsertedId(table, lastInsertedId);
 | 
						|
  } else {
 | 
						|
    nsNavHistory::StoreLastInsertedId(table, lastInsertedId);
 | 
						|
  }
 | 
						|
 | 
						|
  RefPtr<nsVariant> result = new nsVariant();
 | 
						|
  rv = result->SetAsInt64(lastInsertedId);
 | 
						|
  NS_ENSURE_SUCCESS(rv, rv);
 | 
						|
  result.forget(_result);
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
////////////////////////////////////////////////////////////////////////////////
 | 
						|
//// Get Query Param Function
 | 
						|
 | 
						|
/* static */
 | 
						|
nsresult GetQueryParamFunction::create(mozIStorageConnection* aDBConn) {
 | 
						|
  RefPtr<GetQueryParamFunction> function = new GetQueryParamFunction();
 | 
						|
  return aDBConn->CreateFunction("get_query_param"_ns, 2, function);
 | 
						|
}
 | 
						|
 | 
						|
NS_IMPL_ISUPPORTS(GetQueryParamFunction, mozIStorageFunction)
 | 
						|
 | 
						|
NS_IMETHODIMP
 | 
						|
GetQueryParamFunction::OnFunctionCall(mozIStorageValueArray* aArguments,
 | 
						|
                                      nsIVariant** _result) {
 | 
						|
  // Must have non-null function arguments.
 | 
						|
  MOZ_ASSERT(aArguments);
 | 
						|
 | 
						|
  nsDependentCString queryString = getSharedUTF8String(aArguments, 0);
 | 
						|
  nsDependentCString paramName = getSharedUTF8String(aArguments, 1);
 | 
						|
 | 
						|
  RefPtr<nsVariant> result = new nsVariant();
 | 
						|
  if (!queryString.IsEmpty() && !paramName.IsEmpty()) {
 | 
						|
    GetQueryParamIterator iterator(paramName, result);
 | 
						|
    URLParams::Parse(queryString, iterator);
 | 
						|
  }
 | 
						|
 | 
						|
  result.forget(_result);
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
////////////////////////////////////////////////////////////////////////////////
 | 
						|
//// Hash Function
 | 
						|
 | 
						|
/* static */
 | 
						|
nsresult HashFunction::create(mozIStorageConnection* aDBConn) {
 | 
						|
  RefPtr<HashFunction> function = new HashFunction();
 | 
						|
  return aDBConn->CreateFunction("hash"_ns, -1, function);
 | 
						|
}
 | 
						|
 | 
						|
NS_IMPL_ISUPPORTS(HashFunction, mozIStorageFunction)
 | 
						|
 | 
						|
NS_IMETHODIMP
 | 
						|
HashFunction::OnFunctionCall(mozIStorageValueArray* aArguments,
 | 
						|
                             nsIVariant** _result) {
 | 
						|
  // Must have non-null function arguments.
 | 
						|
  MOZ_ASSERT(aArguments);
 | 
						|
 | 
						|
  // Fetch arguments.  Use default values if they were omitted.
 | 
						|
  uint32_t numEntries;
 | 
						|
  nsresult rv = aArguments->GetNumEntries(&numEntries);
 | 
						|
  NS_ENSURE_SUCCESS(rv, rv);
 | 
						|
  NS_ENSURE_TRUE(numEntries >= 1 && numEntries <= 2, NS_ERROR_FAILURE);
 | 
						|
 | 
						|
  nsDependentCString str = getSharedUTF8String(aArguments, 0);
 | 
						|
  nsAutoCString mode;
 | 
						|
  if (numEntries > 1) {
 | 
						|
    aArguments->GetUTF8String(1, mode);
 | 
						|
  }
 | 
						|
 | 
						|
  RefPtr<nsVariant> result = new nsVariant();
 | 
						|
  uint64_t hash;
 | 
						|
  rv = HashURL(str, mode, &hash);
 | 
						|
  NS_ENSURE_SUCCESS(rv, rv);
 | 
						|
  rv = result->SetAsInt64(hash);
 | 
						|
  NS_ENSURE_SUCCESS(rv, rv);
 | 
						|
 | 
						|
  result.forget(_result);
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
////////////////////////////////////////////////////////////////////////////////
 | 
						|
//// Get prefix function
 | 
						|
 | 
						|
/* static */
 | 
						|
nsresult GetPrefixFunction::create(mozIStorageConnection* aDBConn) {
 | 
						|
  RefPtr<GetPrefixFunction> function = new GetPrefixFunction();
 | 
						|
  nsresult rv = aDBConn->CreateFunction("get_prefix"_ns, 1, function);
 | 
						|
  NS_ENSURE_SUCCESS(rv, rv);
 | 
						|
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
NS_IMPL_ISUPPORTS(GetPrefixFunction, mozIStorageFunction)
 | 
						|
 | 
						|
NS_IMETHODIMP
 | 
						|
GetPrefixFunction::OnFunctionCall(mozIStorageValueArray* aArgs,
 | 
						|
                                  nsIVariant** _result) {
 | 
						|
  MOZ_ASSERT(aArgs);
 | 
						|
 | 
						|
  uint32_t numArgs;
 | 
						|
  nsresult rv = aArgs->GetNumEntries(&numArgs);
 | 
						|
  NS_ENSURE_SUCCESS(rv, rv);
 | 
						|
  MOZ_ASSERT(numArgs == 1);
 | 
						|
 | 
						|
  nsDependentCString spec(getSharedUTF8String(aArgs, 0));
 | 
						|
 | 
						|
  RefPtr<nsVariant> result = new nsVariant();
 | 
						|
  result->SetAsACString(Substring(spec, 0, getPrefixLength(spec)));
 | 
						|
  result.forget(_result);
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
////////////////////////////////////////////////////////////////////////////////
 | 
						|
//// Get host and port function
 | 
						|
 | 
						|
/* static */
 | 
						|
nsresult GetHostAndPortFunction::create(mozIStorageConnection* aDBConn) {
 | 
						|
  RefPtr<GetHostAndPortFunction> function = new GetHostAndPortFunction();
 | 
						|
  nsresult rv = aDBConn->CreateFunction("get_host_and_port"_ns, 1, function);
 | 
						|
  NS_ENSURE_SUCCESS(rv, rv);
 | 
						|
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
NS_IMPL_ISUPPORTS(GetHostAndPortFunction, mozIStorageFunction)
 | 
						|
 | 
						|
NS_IMETHODIMP
 | 
						|
GetHostAndPortFunction::OnFunctionCall(mozIStorageValueArray* aArgs,
 | 
						|
                                       nsIVariant** _result) {
 | 
						|
  MOZ_ASSERT(aArgs);
 | 
						|
 | 
						|
  uint32_t numArgs;
 | 
						|
  nsresult rv = aArgs->GetNumEntries(&numArgs);
 | 
						|
  NS_ENSURE_SUCCESS(rv, rv);
 | 
						|
  MOZ_ASSERT(numArgs == 1);
 | 
						|
 | 
						|
  nsDependentCString spec(getSharedUTF8String(aArgs, 0));
 | 
						|
 | 
						|
  RefPtr<nsVariant> result = new nsVariant();
 | 
						|
 | 
						|
  size_type length;
 | 
						|
  size_type index = indexOfHostAndPort(spec, &length);
 | 
						|
  result->SetAsACString(Substring(spec, index, length));
 | 
						|
  result.forget(_result);
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
////////////////////////////////////////////////////////////////////////////////
 | 
						|
//// Strip prefix and userinfo function
 | 
						|
 | 
						|
/* static */
 | 
						|
nsresult StripPrefixAndUserinfoFunction::create(
 | 
						|
    mozIStorageConnection* aDBConn) {
 | 
						|
  RefPtr<StripPrefixAndUserinfoFunction> function =
 | 
						|
      new StripPrefixAndUserinfoFunction();
 | 
						|
  nsresult rv =
 | 
						|
      aDBConn->CreateFunction("strip_prefix_and_userinfo"_ns, 1, function);
 | 
						|
  NS_ENSURE_SUCCESS(rv, rv);
 | 
						|
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
NS_IMPL_ISUPPORTS(StripPrefixAndUserinfoFunction, mozIStorageFunction)
 | 
						|
 | 
						|
NS_IMETHODIMP
 | 
						|
StripPrefixAndUserinfoFunction::OnFunctionCall(mozIStorageValueArray* aArgs,
 | 
						|
                                               nsIVariant** _result) {
 | 
						|
  MOZ_ASSERT(aArgs);
 | 
						|
 | 
						|
  uint32_t numArgs;
 | 
						|
  nsresult rv = aArgs->GetNumEntries(&numArgs);
 | 
						|
  NS_ENSURE_SUCCESS(rv, rv);
 | 
						|
  MOZ_ASSERT(numArgs == 1);
 | 
						|
 | 
						|
  nsDependentCString spec(getSharedUTF8String(aArgs, 0));
 | 
						|
 | 
						|
  RefPtr<nsVariant> result = new nsVariant();
 | 
						|
 | 
						|
  size_type index = indexOfHostAndPort(spec, NULL);
 | 
						|
  result->SetAsACString(Substring(spec, index, spec.Length() - index));
 | 
						|
  result.forget(_result);
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
////////////////////////////////////////////////////////////////////////////////
 | 
						|
//// Is frecency decaying function
 | 
						|
 | 
						|
/* static */
 | 
						|
nsresult IsFrecencyDecayingFunction::create(mozIStorageConnection* aDBConn) {
 | 
						|
  RefPtr<IsFrecencyDecayingFunction> function =
 | 
						|
      new IsFrecencyDecayingFunction();
 | 
						|
  nsresult rv = aDBConn->CreateFunction("is_frecency_decaying"_ns, 0, function);
 | 
						|
  NS_ENSURE_SUCCESS(rv, rv);
 | 
						|
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
NS_IMPL_ISUPPORTS(IsFrecencyDecayingFunction, mozIStorageFunction)
 | 
						|
 | 
						|
NS_IMETHODIMP
 | 
						|
IsFrecencyDecayingFunction::OnFunctionCall(mozIStorageValueArray* aArgs,
 | 
						|
                                           nsIVariant** _result) {
 | 
						|
  MOZ_ASSERT(aArgs);
 | 
						|
 | 
						|
  uint32_t numArgs;
 | 
						|
  nsresult rv = aArgs->GetNumEntries(&numArgs);
 | 
						|
  NS_ENSURE_SUCCESS(rv, rv);
 | 
						|
  MOZ_ASSERT(numArgs == 0);
 | 
						|
 | 
						|
  const nsNavHistory* navHistory = nsNavHistory::GetConstHistoryService();
 | 
						|
  NS_ENSURE_STATE(navHistory);
 | 
						|
 | 
						|
  RefPtr<nsVariant> result = new nsVariant();
 | 
						|
  rv = result->SetAsBool(navHistory->IsFrecencyDecaying());
 | 
						|
  NS_ENSURE_SUCCESS(rv, rv);
 | 
						|
  result.forget(_result);
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
////////////////////////////////////////////////////////////////////////////////
 | 
						|
//// sqrt function
 | 
						|
 | 
						|
/* static */
 | 
						|
nsresult SqrtFunction::create(mozIStorageConnection* aDBConn) {
 | 
						|
  RefPtr<SqrtFunction> function = new SqrtFunction();
 | 
						|
  nsresult rv = aDBConn->CreateFunction("sqrt"_ns, 1, function);
 | 
						|
  NS_ENSURE_SUCCESS(rv, rv);
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
NS_IMPL_ISUPPORTS(SqrtFunction, mozIStorageFunction)
 | 
						|
 | 
						|
NS_IMETHODIMP
 | 
						|
SqrtFunction::OnFunctionCall(mozIStorageValueArray* aArgs,
 | 
						|
                             nsIVariant** _result) {
 | 
						|
  MOZ_ASSERT(aArgs);
 | 
						|
 | 
						|
  uint32_t numArgs;
 | 
						|
  nsresult rv = aArgs->GetNumEntries(&numArgs);
 | 
						|
  NS_ENSURE_SUCCESS(rv, rv);
 | 
						|
  MOZ_ASSERT(numArgs == 1);
 | 
						|
 | 
						|
  double value = aArgs->AsDouble(0);
 | 
						|
 | 
						|
  RefPtr<nsVariant> result = new nsVariant();
 | 
						|
  rv = result->SetAsDouble(sqrt(value));
 | 
						|
  NS_ENSURE_SUCCESS(rv, rv);
 | 
						|
  result.forget(_result);
 | 
						|
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
////////////////////////////////////////////////////////////////////////////////
 | 
						|
//// Note Sync Change Function
 | 
						|
 | 
						|
/* static */
 | 
						|
nsresult NoteSyncChangeFunction::create(mozIStorageConnection* aDBConn) {
 | 
						|
  RefPtr<NoteSyncChangeFunction> function = new NoteSyncChangeFunction();
 | 
						|
  nsresult rv = aDBConn->CreateFunction("note_sync_change"_ns, 0, function);
 | 
						|
  NS_ENSURE_SUCCESS(rv, rv);
 | 
						|
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
NS_IMPL_ISUPPORTS(NoteSyncChangeFunction, mozIStorageFunction)
 | 
						|
 | 
						|
NS_IMETHODIMP
 | 
						|
NoteSyncChangeFunction::OnFunctionCall(mozIStorageValueArray* aArgs,
 | 
						|
                                       nsIVariant** _result) {
 | 
						|
  nsNavBookmarks::NoteSyncChange();
 | 
						|
  *_result = nullptr;
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
}  // namespace places
 | 
						|
}  // namespace mozilla
 |