NS_IMETHODIMP
nsFaviconService::SetFaviconData(nsIURI* aFaviconURI, const PRUint8* aData,
                                 PRUint32 aDataLen, const nsACString& aMimeType,
                                 PRTime aExpiration)
{
  nsresult rv;
  PRUint32 dataLen = aDataLen;
  const PRUint8* data = aData;
  const nsACString* mimeType = &aMimeType;
  nsCString newData, newMimeType;

  // If the page provided a large image for the favicon (eg, a highres image
  // or a multiresolution .ico file), we don't want to store more data than
  // needed. An uncompressed 16x16 RGBA image is 1024 bytes, and almost all
  // sensible 16x16 icons are under 1024 bytes.
  if (aDataLen > 1024) {
    rv = OptimizeFaviconImage(aData, aDataLen, aMimeType, newData, newMimeType);
    if (NS_SUCCEEDED(rv) && newData.Length() < aDataLen) {
      data = reinterpret_cast<PRUint8*>(const_cast<char*>(newData.get())),
      dataLen = newData.Length();
      mimeType = &newMimeType;
    }
  }

  mozIStorageStatement* statement;
  {
    // this block forces the scoper to reset our statement: necessary for the
    // next statement
    mozStorageStatementScoper scoper(mDBGetIconInfo);
    rv = BindStatementURI(mDBGetIconInfo, 0, aFaviconURI);
    NS_ENSURE_SUCCESS(rv, rv);

    PRBool hasResult;
    rv = mDBGetIconInfo->ExecuteStep(&hasResult);
    NS_ENSURE_SUCCESS(rv, rv);

    if (hasResult) {
      // update old one (statement parameter 0 = ID)
      PRInt64 id;
      rv = mDBGetIconInfo->GetInt64(0, &id);
      NS_ENSURE_SUCCESS(rv, rv);
      statement = mDBUpdateIcon;
      rv = statement->BindInt64Parameter(0, id);
    } else {
      // insert new one (statement parameter 0 = favicon URL)
      statement = mDBInsertIcon;
      rv = BindStatementURI(statement, 0, aFaviconURI);
    }
    NS_ENSURE_SUCCESS(rv, rv);
  }

  mozStorageStatementScoper scoper(statement);

  // the insert and update statements share all but the 0th parameter
  rv = statement->BindBlobParameter(1, data, dataLen);
  NS_ENSURE_SUCCESS(rv, rv);
  rv = statement->BindUTF8StringParameter(2, *mimeType);
  NS_ENSURE_SUCCESS(rv, rv);
  rv = statement->BindInt64Parameter(3, aExpiration);
  NS_ENSURE_SUCCESS(rv, rv);
  return statement->Execute();
}
RenderObject* RenderObjectChildList::removeChildNode(RenderObject* owner, RenderObject* oldChild, bool notifyRenderer)
{
    ASSERT(oldChild->parent() == owner);

    if (oldChild->isFloatingOrOutOfFlowPositioned())
        toRenderBox(oldChild)->removeFloatingOrPositionedChildFromBlockLists();

    {
        // FIXME: We should not be allowing repaint during layout. crbug.com/336250
        AllowPaintInvalidationScope scoper(owner->frameView());

        // So that we'll get the appropriate dirty bit set (either that a normal flow child got yanked or
        // that a positioned child got yanked). We also repaint, so that the area exposed when the child
        // disappears gets repainted properly.
        if (!owner->documentBeingDestroyed() && notifyRenderer && oldChild->everHadLayout()) {
            oldChild->setNeedsLayoutAndPrefWidthsRecalcAndFullPaintInvalidation();
            // We only repaint |oldChild| if we have a RenderLayer as its visual overflow may not be tracked by its parent.
            if (oldChild->isBody())
                owner->view()->paintInvalidationForWholeRenderer();
            else
                oldChild->paintInvalidationForWholeRenderer();
        }
    }

    // If we have a line box wrapper, delete it.
    if (oldChild->isBox())
        toRenderBox(oldChild)->deleteLineBoxWrapper();

    // If oldChild is the start or end of the selection, then clear the selection to
    // avoid problems of invalid pointers.
    // FIXME: The FrameSelection should be responsible for this when it
    // is notified of DOM mutations.
    if (!owner->documentBeingDestroyed() && oldChild->isSelectionBorder())
        owner->view()->clearSelection();

    if (!owner->documentBeingDestroyed() && notifyRenderer)
        oldChild->willBeRemovedFromTree();

    // WARNING: There should be no code running between willBeRemovedFromTree and the actual removal below.
    // This is needed to avoid race conditions where willBeRemovedFromTree would dirty the tree's structure
    // and the code running here would force an untimely rebuilding, leaving |oldChild| dangling.

    if (oldChild->previousSibling())
        oldChild->previousSibling()->setNextSibling(oldChild->nextSibling());
    if (oldChild->nextSibling())
        oldChild->nextSibling()->setPreviousSibling(oldChild->previousSibling());

    if (firstChild() == oldChild)
        setFirstChild(oldChild->nextSibling());
    if (lastChild() == oldChild)
        setLastChild(oldChild->previousSibling());

    oldChild->setPreviousSibling(0);
    oldChild->setNextSibling(0);
    oldChild->setParent(0);

    // rendererRemovedFromTree walks the whole subtree. We can improve performance
    // by skipping this step when destroying the entire tree.
    if (!owner->documentBeingDestroyed())
        RenderCounter::rendererRemovedFromTree(oldChild);

    if (AXObjectCache* cache = owner->document().existingAXObjectCache())
        cache->childrenChanged(owner);

    return oldChild;
}
nsresult
nsFaviconService::SetFaviconUrlForPageInternal(nsIURI* aPageURI,
                                               nsIURI* aFaviconURI,
                                               PRBool* aHasData,
                                               PRTime* aExpiration)
{
  nsresult rv;
  PRInt64 iconId = -1;
  mozStorageTransaction transaction(mDBConn, PR_FALSE);
  {
    mozStorageStatementScoper scoper(mDBGetIconInfo);
    rv = BindStatementURI(mDBGetIconInfo, 0, aFaviconURI);
    NS_ENSURE_SUCCESS(rv, rv);

    PRBool hasResult = PR_FALSE;
    if (NS_SUCCEEDED(mDBGetIconInfo->ExecuteStep(&hasResult)) && hasResult) {
      // We already have an entry for this icon, just get the stats
      rv = mDBGetIconInfo->GetInt64(0, &iconId);
      NS_ENSURE_SUCCESS(rv, rv);

      // see if this icon has data already
      PRInt32 dataSize;
      rv = mDBGetIconInfo->GetInt32(1, &dataSize);
      NS_ENSURE_SUCCESS(rv, rv);
      if (dataSize > 0)
        *aHasData = PR_TRUE;

      // expiration
      rv = mDBGetIconInfo->GetInt64(2, aExpiration);
      NS_ENSURE_SUCCESS(rv, rv);
    }
  }

  if (iconId == -1) {
    // We did not find any entry, so create a new one
    *aHasData = PR_FALSE;
    *aExpiration = 0;

    // not-binded params are automatically nullified by mozStorage
    mozStorageStatementScoper scoper(mDBInsertIcon);
    rv = BindStatementURI(mDBInsertIcon, 0, aFaviconURI);
    NS_ENSURE_SUCCESS(rv, rv);

    rv = mDBInsertIcon->Execute();
    NS_ENSURE_SUCCESS(rv, rv);

    rv = mDBConn->GetLastInsertRowID(&iconId);
    NS_ENSURE_SUCCESS(rv, rv);
  }

  // now link our icon entry with the page
  nsNavHistory* historyService = nsNavHistory::GetHistoryService();
  NS_ENSURE_TRUE(historyService, NS_ERROR_OUT_OF_MEMORY);

  PRInt64 pageId;
  rv = historyService->GetUrlIdFor(aPageURI, &pageId, PR_TRUE);
  NS_ENSURE_SUCCESS(rv, rv);

  mozStorageStatementScoper scoper(mDBSetPageFavicon);
  rv = mDBSetPageFavicon->BindInt64Parameter(0, pageId);
  NS_ENSURE_SUCCESS(rv, rv);
  rv = mDBSetPageFavicon->BindInt64Parameter(1, iconId);
  NS_ENSURE_SUCCESS(rv, rv);
  rv = mDBSetPageFavicon->Execute();
  NS_ENSURE_SUCCESS(rv, rv);

  rv = transaction.Commit();
  NS_ENSURE_SUCCESS(rv, rv);
  return NS_OK;
}
nsresult
nsFaviconService::DoSetAndLoadFaviconForPage(nsIURI* aPageURI,
                                             nsIURI* aFaviconURI,
                                             PRBool aForceReload)
{
  nsCOMPtr<nsIURI> page(aPageURI);

  // don't load favicons when history is disabled
  nsNavHistory* history = nsNavHistory::GetHistoryService();
  NS_ENSURE_TRUE(history, NS_ERROR_FAILURE);
  if (history->IsHistoryDisabled()) {
    // history is disabled - check to see if this favicon could be for a
    // bookmark
    nsNavBookmarks* bookmarks = nsNavBookmarks::GetBookmarksService();
    NS_ENSURE_TRUE(bookmarks, NS_ERROR_UNEXPECTED);

    nsCOMPtr<nsIURI> bookmarkURI;
    nsresult rv = bookmarks->GetBookmarkedURIFor(aPageURI,
                                                 getter_AddRefs(bookmarkURI));
    NS_ENSURE_SUCCESS(rv, rv);
    if (! bookmarkURI) {
      // page is not bookmarked, don't save favicon
      return NS_OK;
    }

    // page is bookmarked, set the URI to be the bookmark, the bookmark URI
    // might be different than our input URI if there was a redirect, so
    // always use the bookmark URI here to avoid setting the favicon for
    // non-bookmarked pages.
    page = bookmarkURI;
  }

  // check the failed favicon cache
  PRBool previouslyFailed;
  nsresult rv = IsFailedFavicon(aFaviconURI, &previouslyFailed);
  NS_ENSURE_SUCCESS(rv, rv);
  if (previouslyFailed) {
    if (aForceReload)
      RemoveFailedFavicon(aFaviconURI); // force reload clears from failed cache
    else
      return NS_OK; // ignore previously failed favicons
  }

  // filter out bad URLs
  PRBool canAdd;
  rv = history->CanAddURI(page, &canAdd);
  NS_ENSURE_SUCCESS(rv, rv);
  if (! canAdd)
    return NS_OK; // ignore favicons for this url

  // If have an image loaded in the main frame, that image will get set as its
  // own favicon. It would be nice to store a resampled version of the image,
  // but that's prohibitive for now. This workaround just refuses to save the
  // favicon in this case.
  PRBool pageEqualsFavicon;
  rv = page->Equals(aFaviconURI, &pageEqualsFavicon);
  NS_ENSURE_SUCCESS(rv, rv);
  if (pageEqualsFavicon)
    return NS_OK;

  // ignore error page favicons.
  nsCOMPtr<nsIURI> errorPageFavicon;
  rv = NS_NewURI(getter_AddRefs(errorPageFavicon),
                 NS_LITERAL_CSTRING(FAVICON_ERRORPAGE_URL));
  NS_ENSURE_SUCCESS(rv, rv);
  PRBool isErrorPage;
  rv = aFaviconURI->Equals(errorPageFavicon, &isErrorPage);
  NS_ENSURE_SUCCESS(rv, rv);
  if (isErrorPage)
    return NS_OK;

  // See if we have data and get the expiration time for this favicon. It might
  // be nice to say SetFaviconUrlForPageInternal here but we DON'T want to set
  // the favicon for the page unless we know we have it. For example, if I go
  // to random site x.com, the browser will still tell us that the favicon is
  // x.com/favicon.ico even if there is no such file. We don't want to pollute
  // our tables with this useless data.
  PRBool hasData = PR_FALSE;
  PRTime expiration = 0;
  { // scope for statement
    mozStorageStatementScoper scoper(mDBGetIconInfo);
    rv = BindStatementURI(mDBGetIconInfo, 0, aFaviconURI);
    NS_ENSURE_SUCCESS(rv, rv);

    PRBool hasMatch;
    rv = mDBGetIconInfo->ExecuteStep(&hasMatch);
    NS_ENSURE_SUCCESS(rv, rv);
    if (hasMatch) {
      PRInt32 dataSize;
      mDBGetIconInfo->GetInt32(1, &dataSize);
      hasData = dataSize > 0;
      mDBGetIconInfo->GetInt64(2, &expiration);
    }
  }

  // See if this favicon has expired yet. We don't want to waste time reloading
  // from the web or cache if we have a recent version.
  PRTime now = PR_Now();
  if (hasData && now < expiration && ! aForceReload) {
    // data still valid, no need to reload

    // For page revisits (pretty common) we DON'T want to send out any
    // notifications if we've already set the favicon. These notifications will
    // cause parts of the UI to update and will be slow. This also saves us a
    // database write in these cases.
    nsCOMPtr<nsIURI> oldFavicon;
    PRBool faviconsEqual;
    if (NS_SUCCEEDED(GetFaviconForPage(page, getter_AddRefs(oldFavicon))) &&
        NS_SUCCEEDED(aFaviconURI->Equals(oldFavicon, &faviconsEqual)) &&
        faviconsEqual)
      return NS_OK; // already set

    // This will associate the favicon URL with the page.
    rv = SetFaviconUrlForPageInternal(page, aFaviconURI, &hasData, &expiration);
    NS_ENSURE_SUCCESS(rv, rv);

    SendFaviconNotifications(page, aFaviconURI);
    UpdateBookmarkRedirectFavicon(page, aFaviconURI);
    return NS_OK;
  }

  nsCOMPtr<nsIIOService> ioservice = do_GetIOService(&rv);
  NS_ENSURE_SUCCESS(rv, rv);

  nsCOMPtr<nsIChannel> channel;
  rv = ioservice->NewChannelFromURI(aFaviconURI, getter_AddRefs(channel));
  NS_ENSURE_SUCCESS(rv, rv);

  nsCOMPtr<nsIStreamListener> listener =
    new FaviconLoadListener(this, page, aFaviconURI, channel);
  NS_ENSURE_TRUE(listener, NS_ERROR_OUT_OF_MEMORY);
  nsCOMPtr<nsIInterfaceRequestor> listenerRequestor =
    do_QueryInterface(listener, &rv);
  NS_ENSURE_SUCCESS(rv, rv);

  rv = channel->SetNotificationCallbacks(listenerRequestor);
  NS_ENSURE_SUCCESS(rv, rv);
  rv = channel->AsyncOpen(listener, nsnull);
  NS_ENSURE_SUCCESS(rv, rv);

  // DB will be update and observers will be notified when the data has
  // finished loading
  return NS_OK;
}
nsresult
GetAllHelper::DoDatabaseWork(mozIStorageConnection* /* aConnection */)
{
  nsCString indexTable;
  if (mIndex->IsUnique()) {
    indexTable.AssignLiteral("unique_index_data");
  }
  else {
    indexTable.AssignLiteral("index_data");
  }

  nsCString keyRangeClause;
  if (mKeyRange) {
    mKeyRange->GetBindingClause(NS_LITERAL_CSTRING("value"), keyRangeClause);
  }

  nsCString limitClause;
  if (mLimit != PR_UINT32_MAX) {
    limitClause = NS_LITERAL_CSTRING(" LIMIT ");
    limitClause.AppendInt(mLimit);
  }

  nsCString query = NS_LITERAL_CSTRING("SELECT data, file_ids FROM object_data "
                                       "INNER JOIN ") + indexTable +
                    NS_LITERAL_CSTRING(" AS index_table ON object_data.id = "
                                       "index_table.object_data_id "
                                       "WHERE index_id = :index_id") +
                    keyRangeClause + limitClause;

  nsCOMPtr<mozIStorageStatement> stmt = mTransaction->GetCachedStatement(query);
  NS_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);

  mozStorageStatementScoper scoper(stmt);

  nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("index_id"),
                                      mIndex->Id());
  NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);

  if (mKeyRange) {
    rv = mKeyRange->BindToStatement(stmt);
    NS_ENSURE_SUCCESS(rv, rv);
  }

  mCloneReadInfos.SetCapacity(50);

  bool hasResult;
  while(NS_SUCCEEDED((rv = stmt->ExecuteStep(&hasResult))) && hasResult) {
    if (mCloneReadInfos.Capacity() == mCloneReadInfos.Length()) {
      mCloneReadInfos.SetCapacity(mCloneReadInfos.Capacity() * 2);
    }

    StructuredCloneReadInfo* readInfo = mCloneReadInfos.AppendElement();
    NS_ASSERTION(readInfo, "This shouldn't fail!");

    rv = IDBObjectStore::GetStructuredCloneReadInfoFromStatement(stmt, 0, 1,
      mDatabase->Manager(), *readInfo);
    NS_ENSURE_SUCCESS(rv, rv);
  }
  NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);

  return NS_OK;
}
nsresult purpleDNSRequest::OnLookupComplete(nsICancelable *request,
                                            nsIDNSRecord *record,
                                            nsresult status)
{
  NS_ASSERTION(NS_IsMainThread(), "wrong thread");
  NS_ASSERTION(request == asyncResolv, "wrong request");

  purpleAccountScoper scoper(mAccountId);

  if (NS_FAILED(status)) {
    Failed("DNS query failed\n");
    return NS_OK;
  }

  GSList *hosts = NULL;
  bool more;
  record->HasMore(&more);
  if (!more) {
    Failed("Not found\n");
    return NS_OK;
  }

  if (NS_FAILED(purpleDNS::Remove(query_data))) {
    LOG(("DNS resolution completed but result discarded because it was cancelled"));
    return NS_OK;
  }

  mozilla::net::NetAddr netAddr;
  for (; more; record->HasMore(&more))
  {
    unsigned short port = purple_dnsquery_get_port(query_data);
    if (NS_FAILED(record->GetNextAddr(port, &netAddr))) {
      Failed("GetNextAddr failed\n");
      return NS_OK;
    }

    socklen_t addrlen;
    struct sockaddr *addr;

    if (netAddr.raw.family == AF_INET) {
      addrlen = sizeof(struct sockaddr_in);
      struct sockaddr_in *s_in = (struct sockaddr_in *)g_malloc0(addrlen);
      s_in->sin_port = netAddr.inet.port;
      memcpy(&s_in->sin_addr, &netAddr.inet.ip, sizeof(netAddr.inet.ip));
      addr = (struct sockaddr *)s_in;
      LOG(("Found ip v4 address"));
    }
    else if (netAddr.raw.family == AF_INET6) {
      addrlen = sizeof(struct sockaddr_in6);
      struct sockaddr_in6 *s_in6 = (struct sockaddr_in6 *)g_malloc0(addrlen);
      s_in6->sin6_port = netAddr.inet6.port;
      s_in6->sin6_flowinfo = netAddr.inet6.flowinfo;
      memcpy(&s_in6->sin6_addr, &netAddr.inet6.ip, sizeof(netAddr.inet6.ip.u8));
      s_in6->sin6_scope_id = netAddr.inet6.scope_id;
      addr = (struct sockaddr *)s_in6;
      LOG(("Found ip v6 address"));
    }
#ifndef XP_WIN
    else if (netAddr.raw.family == AF_LOCAL) {
      addrlen = sizeof(struct sockaddr_un);
      struct sockaddr_un *s_un = (struct sockaddr_un *)g_malloc0(addrlen);
      memcpy(&s_un->sun_path, netAddr.local.path, sizeof(netAddr.local.path));
      addr = (struct sockaddr *)s_un;
      LOG(("Found local address"));
    }
#endif
    else {
      NS_NOTREACHED("Unknown address type...");
      continue;
    }

    addr->sa_family = netAddr.raw.family;

    hosts = g_slist_append(hosts, GINT_TO_POINTER(addrlen));
    hosts = g_slist_append(hosts, addr);
  }
  LOG(("DNS resolution done"));
  resolved_cb(query_data, hosts);
  return NS_OK;
}
nsresult
OpenCursorHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
{
  NS_ASSERTION(aConnection, "Passed a null connection!");

  nsCString indexTable;
  if (mIndex->IsUnique()) {
    indexTable.AssignLiteral("unique_index_data");
  }
  else {
    indexTable.AssignLiteral("index_data");
  }

  NS_NAMED_LITERAL_CSTRING(value, "index_table.value");

  nsCString keyRangeClause;
  if (mKeyRange) {
    mKeyRange->GetBindingClause(value, keyRangeClause);
  }

  nsCAutoString directionClause(" ORDER BY index_table.value ");
  switch (mDirection) {
    case nsIIDBCursor::NEXT:
    case nsIIDBCursor::NEXT_NO_DUPLICATE:
      directionClause +=
        NS_LITERAL_CSTRING("ASC, index_table.object_data_key ASC");
      break;

    case nsIIDBCursor::PREV:
      directionClause +=
        NS_LITERAL_CSTRING("DESC, index_table.object_data_key DESC");
      break;

    case nsIIDBCursor::PREV_NO_DUPLICATE:
      directionClause +=
        NS_LITERAL_CSTRING("DESC, index_table.object_data_key ASC");
      break;

    default:
      NS_NOTREACHED("Unknown direction!");
  }

  nsCString firstQuery =
    NS_LITERAL_CSTRING("SELECT index_table.value, "
                       "index_table.object_data_key, object_data.data, "
                       "object_data.file_ids FROM ") +
    indexTable +
    NS_LITERAL_CSTRING(" AS index_table INNER JOIN object_data ON "
                       "index_table.object_data_id = object_data.id "
                       "WHERE index_table.index_id = :id") +
    keyRangeClause + directionClause +
    NS_LITERAL_CSTRING(" LIMIT 1");

  nsCOMPtr<mozIStorageStatement> stmt =
    mTransaction->GetCachedStatement(firstQuery);
  NS_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);

  mozStorageStatementScoper scoper(stmt);

  nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("id"), mIndex->Id());
  NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);

  if (mKeyRange) {
    rv = mKeyRange->BindToStatement(stmt);
    NS_ENSURE_SUCCESS(rv, rv);
  }

  bool hasResult;
  rv = stmt->ExecuteStep(&hasResult);
  NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);

  if (!hasResult) {
    mKey.Unset();
    return NS_OK;
  }

  rv = mKey.SetFromStatement(stmt, 0);
  NS_ENSURE_SUCCESS(rv, rv);

  rv = mObjectKey.SetFromStatement(stmt, 1);
  NS_ENSURE_SUCCESS(rv, rv);

  rv = IDBObjectStore::GetStructuredCloneReadInfoFromStatement(stmt, 2, 3,
    mDatabase->Manager(), mCloneReadInfo);
  NS_ENSURE_SUCCESS(rv, rv);

  // Now we need to make the query to get the next match.
  nsCAutoString queryStart =
    NS_LITERAL_CSTRING("SELECT index_table.value, "
                       "index_table.object_data_key, object_data.data, "
                       "object_data.file_ids FROM ") +
    indexTable +
    NS_LITERAL_CSTRING(" AS index_table INNER JOIN object_data ON "
                       "index_table.object_data_id = object_data.id "
                       "WHERE index_table.index_id = :id");

  NS_NAMED_LITERAL_CSTRING(rangeKey, "range_key");

  NS_NAMED_LITERAL_CSTRING(limit, " LIMIT ");

  switch (mDirection) {
    case nsIIDBCursor::NEXT:
      if (mKeyRange && !mKeyRange->Upper().IsUnset()) {
        AppendConditionClause(value, rangeKey, true, !mKeyRange->IsUpperOpen(),
                              queryStart);
        mRangeKey = mKeyRange->Upper();
      }
      mContinueQuery =
        queryStart +
        NS_LITERAL_CSTRING(" AND index_table.value >= :current_key AND "
                           "( index_table.value > :current_key OR "
                           "  index_table.object_data_key > :object_key ) ") +
        directionClause + limit;
      mContinueToQuery =
        queryStart +
        NS_LITERAL_CSTRING(" AND index_table.value >= :current_key") +
        directionClause + limit;
      break;

    case nsIIDBCursor::NEXT_NO_DUPLICATE:
      if (mKeyRange && !mKeyRange->Upper().IsUnset()) {
        AppendConditionClause(value, rangeKey, true, !mKeyRange->IsUpperOpen(),
                              queryStart);
        mRangeKey = mKeyRange->Upper();
      }
      mContinueQuery =
        queryStart +
        NS_LITERAL_CSTRING(" AND index_table.value > :current_key") +
        directionClause + limit;
      mContinueToQuery =
        queryStart +
        NS_LITERAL_CSTRING(" AND index_table.value >= :current_key") +
        directionClause + limit;
      break;

    case nsIIDBCursor::PREV:
      if (mKeyRange && !mKeyRange->Lower().IsUnset()) {
        AppendConditionClause(value, rangeKey, false, !mKeyRange->IsLowerOpen(),
                              queryStart);
        mRangeKey = mKeyRange->Lower();
      }
      mContinueQuery =
        queryStart +
        NS_LITERAL_CSTRING(" AND index_table.value <= :current_key AND "
                           "( index_table.value < :current_key OR "
                           "  index_table.object_data_key < :object_key ) ") +
        directionClause + limit;
      mContinueToQuery =
        queryStart +
        NS_LITERAL_CSTRING(" AND index_table.value <= :current_key") +
        directionClause + limit;
      break;

    case nsIIDBCursor::PREV_NO_DUPLICATE:
      if (mKeyRange && !mKeyRange->Lower().IsUnset()) {
        AppendConditionClause(value, rangeKey, false, !mKeyRange->IsLowerOpen(),
                              queryStart);
        mRangeKey = mKeyRange->Lower();
      }
      mContinueQuery =
        queryStart +
        NS_LITERAL_CSTRING(" AND index_table.value < :current_key") +
        directionClause + limit;
      mContinueToQuery =
        queryStart +
        NS_LITERAL_CSTRING(" AND index_table.value <= :current_key") +
        directionClause + limit;
      break;

    default:
      NS_NOTREACHED("Unknown direction type!");
  }

  return NS_OK;
}
nsresult
GetAllHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
{
  NS_ASSERTION(aConnection, "Passed a null connection!");

  if (!mCloneBuffers.SetCapacity(50)) {
    NS_ERROR("Out of memory!");
    return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
  }

  nsCString dataTableName;
  nsCString objectDataId;
  nsCString indexTableName;

  if (mIndex->IsAutoIncrement()) {
    dataTableName.AssignLiteral("ai_object_data");
    objectDataId.AssignLiteral("ai_object_data_id");
    if (mIndex->IsUnique()) {
      indexTableName.AssignLiteral("ai_unique_index_data");
    }
    else {
      indexTableName.AssignLiteral("ai_index_data");
    }
  }
  else {
    dataTableName.AssignLiteral("object_data");
    objectDataId.AssignLiteral("object_data_id");
    if (mIndex->IsUnique()) {
      indexTableName.AssignLiteral("unique_index_data");
    }
    else {
      indexTableName.AssignLiteral("index_data");
    }
  }

  NS_NAMED_LITERAL_CSTRING(indexId, "index_id");
  NS_NAMED_LITERAL_CSTRING(value, "value");

  nsCString keyClause;
  if (!mKey.IsUnset()) {
    keyClause = NS_LITERAL_CSTRING(" AND ") + value +
                NS_LITERAL_CSTRING(" = :") + value;
  }

  nsCString limitClause;
  if (mLimit != PR_UINT32_MAX) {
    limitClause = NS_LITERAL_CSTRING(" LIMIT ");
    limitClause.AppendInt(mLimit);
  }

  nsCString query = NS_LITERAL_CSTRING("SELECT data FROM ") + dataTableName +
                    NS_LITERAL_CSTRING(" INNER JOIN ") + indexTableName  +
                    NS_LITERAL_CSTRING(" ON ") + dataTableName +
                    NS_LITERAL_CSTRING(".id = ") + indexTableName +
                    NS_LITERAL_CSTRING(".") + objectDataId +
                    NS_LITERAL_CSTRING(" WHERE ") + indexId  +
                    NS_LITERAL_CSTRING(" = :") + indexId + keyClause +
                    limitClause;

  nsCOMPtr<mozIStorageStatement> stmt = mTransaction->GetCachedStatement(query);
  NS_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);

  mozStorageStatementScoper scoper(stmt);

  nsresult rv = stmt->BindInt64ByName(indexId, mIndex->Id());
  NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);

  if (!mKey.IsUnset()) {
    if (mKey.IsInt()) {
      rv = stmt->BindInt64ByName(value, mKey.IntValue());
    }
    else if (mKey.IsString()) {
      rv = stmt->BindStringByName(value, mKey.StringValue());
    }
    else {
      NS_NOTREACHED("Bad key type!");
    }
    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
  }

  PRBool hasResult;
  while(NS_SUCCEEDED((rv = stmt->ExecuteStep(&hasResult))) && hasResult) {
    if (mCloneBuffers.Capacity() == mCloneBuffers.Length()) {
      if (!mCloneBuffers.SetCapacity(mCloneBuffers.Capacity() * 2)) {
        NS_ERROR("Out of memory!");
        return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
      }
    }

    JSAutoStructuredCloneBuffer* buffer = mCloneBuffers.AppendElement();
    NS_ASSERTION(buffer, "This shouldn't fail!");

    rv = IDBObjectStore::GetStructuredCloneDataFromStatement(stmt, 0, *buffer);
    NS_ENSURE_SUCCESS(rv, rv);
  }
  NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);

  return NS_OK;
}
nsresult
GetAllKeysHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
{
  NS_ASSERTION(aConnection, "Passed a null connection!");

  if (!mKeys.SetCapacity(50)) {
    NS_ERROR("Out of memory!");
    return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
  }

  nsCString keyColumn;
  nsCString tableName;

  if (mIndex->IsAutoIncrement()) {
    keyColumn.AssignLiteral("ai_object_data_id");
    if (mIndex->IsUnique()) {
      tableName.AssignLiteral("ai_unique_index_data");
    }
    else {
      tableName.AssignLiteral("ai_index_data");
    }
  }
  else {
    keyColumn.AssignLiteral("object_data_key");
    if (mIndex->IsUnique()) {
      tableName.AssignLiteral("unique_index_data");
    }
    else {
      tableName.AssignLiteral("index_data");
    }
  }

  NS_NAMED_LITERAL_CSTRING(indexId, "index_id");
  NS_NAMED_LITERAL_CSTRING(value, "value");

  nsCString keyClause;
  if (!mKey.IsUnset()) {
    keyClause = NS_LITERAL_CSTRING(" AND ") + value +
                NS_LITERAL_CSTRING(" = :") + value;
  }

  nsCString limitClause;
  if (mLimit != PR_UINT32_MAX) {
    limitClause = NS_LITERAL_CSTRING(" LIMIT ");
    limitClause.AppendInt(mLimit);
  }

  nsCString query = NS_LITERAL_CSTRING("SELECT ") + keyColumn +
                    NS_LITERAL_CSTRING(" FROM ") + tableName +
                    NS_LITERAL_CSTRING(" WHERE ") + indexId  +
                    NS_LITERAL_CSTRING(" = :") + indexId + keyClause +
                    limitClause;
  
  nsCOMPtr<mozIStorageStatement> stmt = mTransaction->GetCachedStatement(query);
  NS_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);

  mozStorageStatementScoper scoper(stmt);

  nsresult rv = stmt->BindInt64ByName(indexId, mIndex->Id());
  NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);

  if (!mKey.IsUnset()) {
    if (mKey.IsInt()) {
      rv = stmt->BindInt64ByName(value, mKey.IntValue());
    }
    else if (mKey.IsString()) {
      rv = stmt->BindStringByName(value, mKey.StringValue());
    }
    else {
      NS_NOTREACHED("Bad key type!");
    }
    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
  }

  PRBool hasResult;
  while(NS_SUCCEEDED((rv = stmt->ExecuteStep(&hasResult))) && hasResult) {
    if (mKeys.Capacity() == mKeys.Length()) {
      if (!mKeys.SetCapacity(mKeys.Capacity() * 2)) {
        NS_ERROR("Out of memory!");
        return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
      }
    }

    Key* key = mKeys.AppendElement();
    NS_ASSERTION(key, "This shouldn't fail!");

    PRInt32 keyType;
    rv = stmt->GetTypeOfIndex(0, &keyType);
    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);

    NS_ASSERTION(keyType == mozIStorageStatement::VALUE_TYPE_INTEGER ||
                 keyType == mozIStorageStatement::VALUE_TYPE_TEXT,
                 "Bad key type!");

    if (keyType == mozIStorageStatement::VALUE_TYPE_INTEGER) {
      *key = stmt->AsInt64(0);
    }
    else if (keyType == mozIStorageStatement::VALUE_TYPE_TEXT) {
      rv = stmt->GetString(0, key->ToString());
      NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
    }
    else {
      NS_NOTREACHED("Bad SQLite type!");
    }
  }
  NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);

  return NS_OK;
}
nsresult
OpenCursorHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
{
  NS_ASSERTION(aConnection, "Passed a null connection!");

  nsCString indexTable;
  nsCString objectTable;
  nsCString objectDataIdColumn;
  nsCString keyValueColumn;

  if (mIndex->IsAutoIncrement()) {
    objectTable.AssignLiteral("ai_object_data");
    objectDataIdColumn.AssignLiteral("ai_object_data_id");
    keyValueColumn.AssignLiteral("id");
    if (mIndex->IsUnique()) {
      indexTable.AssignLiteral("ai_unique_index_data");
    }
    else {
      indexTable.AssignLiteral("ai_index_data");
    }
  }
  else {
    objectTable.AssignLiteral("object_data");
    objectDataIdColumn.AssignLiteral("object_data_id");
    keyValueColumn.AssignLiteral("key_value");
    if (mIndex->IsUnique()) {
      indexTable.AssignLiteral("unique_index_data");
    }
    else {
      indexTable.AssignLiteral("index_data");
    }
  }

  NS_NAMED_LITERAL_CSTRING(id, "id");
  NS_NAMED_LITERAL_CSTRING(lowerKeyName, "lower_key");
  NS_NAMED_LITERAL_CSTRING(upperKeyName, "upper_key");

  nsCString value = indexTable + NS_LITERAL_CSTRING(".value");
  nsCString data = objectTable + NS_LITERAL_CSTRING(".data");
  nsCString keyValue = objectTable + NS_LITERAL_CSTRING(".") + keyValueColumn;

  nsCAutoString keyRangeClause;
  if (!mLowerKey.IsUnset()) {
    AppendConditionClause(value, lowerKeyName, false, !mLowerOpen,
                          keyRangeClause);
  }
  if (!mUpperKey.IsUnset()) {
    AppendConditionClause(value, upperKeyName, true, !mUpperOpen,
                          keyRangeClause);
  }

  nsCAutoString directionClause = NS_LITERAL_CSTRING(" ORDER BY ") + value;
  switch (mDirection) {
    case nsIIDBCursor::NEXT:
    case nsIIDBCursor::NEXT_NO_DUPLICATE:
      directionClause.AppendLiteral(" ASC");
      break;

    case nsIIDBCursor::PREV:
    case nsIIDBCursor::PREV_NO_DUPLICATE:
      directionClause.AppendLiteral(" DESC");
      break;

    default:
      NS_NOTREACHED("Unknown direction!");
  }
  directionClause += NS_LITERAL_CSTRING(", ") + keyValue +
                     NS_LITERAL_CSTRING(" ASC");

  nsCString firstQuery = NS_LITERAL_CSTRING("SELECT ") + value +
                         NS_LITERAL_CSTRING(", ") + keyValue +
                         NS_LITERAL_CSTRING(", ") + data +
                         NS_LITERAL_CSTRING(" FROM ") + objectTable +
                         NS_LITERAL_CSTRING(" INNER JOIN ") + indexTable +
                         NS_LITERAL_CSTRING(" ON ") + indexTable +
                         NS_LITERAL_CSTRING(".") + objectDataIdColumn +
                         NS_LITERAL_CSTRING(" = ") + objectTable +
                         NS_LITERAL_CSTRING(".id WHERE ") + indexTable +
                         NS_LITERAL_CSTRING(".index_id = :id") +
                         keyRangeClause + directionClause +
                         NS_LITERAL_CSTRING(" LIMIT 1");

  nsCOMPtr<mozIStorageStatement> stmt =
    mTransaction->GetCachedStatement(firstQuery);
  NS_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);

  mozStorageStatementScoper scoper(stmt);

  nsresult rv = stmt->BindInt64ByName(id, mIndex->Id());
  NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);

  if (!mLowerKey.IsUnset()) {
    if (mLowerKey.IsString()) {
      rv = stmt->BindStringByName(lowerKeyName, mLowerKey.StringValue());
    }
    else if (mLowerKey.IsInt()) {
      rv = stmt->BindInt64ByName(lowerKeyName, mLowerKey.IntValue());
    }
    else {
      NS_NOTREACHED("Bad key!");
    }
    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
  }

  if (!mUpperKey.IsUnset()) {
    if (mUpperKey.IsString()) {
      rv = stmt->BindStringByName(upperKeyName, mUpperKey.StringValue());
    }
    else if (mUpperKey.IsInt()) {
      rv = stmt->BindInt64ByName(upperKeyName, mUpperKey.IntValue());
    }
    else {
      NS_NOTREACHED("Bad key!");
    }
    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
  }

  PRBool hasResult;
  rv = stmt->ExecuteStep(&hasResult);
  NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);

  if (!hasResult) {
    mKey = Key::UNSETKEY;
    return NS_OK;
  }

  PRInt32 keyType;
  rv = stmt->GetTypeOfIndex(0, &keyType);
  NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);

  NS_ASSERTION(keyType == mozIStorageStatement::VALUE_TYPE_INTEGER ||
               keyType == mozIStorageStatement::VALUE_TYPE_TEXT,
               "Bad key type!");

  if (keyType == mozIStorageStatement::VALUE_TYPE_INTEGER) {
    mKey = stmt->AsInt64(0);
  }
  else if (keyType == mozIStorageStatement::VALUE_TYPE_TEXT) {
    rv = stmt->GetString(0, mKey.ToString());
    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
  }
  else {
    NS_NOTREACHED("Bad SQLite type!");
  }

  rv = stmt->GetTypeOfIndex(1, &keyType);
  NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);

  NS_ASSERTION(keyType == mozIStorageStatement::VALUE_TYPE_INTEGER ||
               keyType == mozIStorageStatement::VALUE_TYPE_TEXT,
               "Bad key type!");

  if (keyType == mozIStorageStatement::VALUE_TYPE_INTEGER) {
    mObjectKey = stmt->AsInt64(1);
  }
  else if (keyType == mozIStorageStatement::VALUE_TYPE_TEXT) {
    rv = stmt->GetString(1, mObjectKey.ToString());
    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
  }
  else {
    NS_NOTREACHED("Bad SQLite type!");
  }

  rv = IDBObjectStore::GetStructuredCloneDataFromStatement(stmt, 2,
                                                           mCloneBuffer);
  NS_ENSURE_SUCCESS(rv, rv);

  // Now we need to make the query to get the next match.
  nsCAutoString queryStart = NS_LITERAL_CSTRING("SELECT ") + value +
                             NS_LITERAL_CSTRING(", ") + keyValue +
                             NS_LITERAL_CSTRING(", ") + data +
                             NS_LITERAL_CSTRING(" FROM ") + objectTable +
                             NS_LITERAL_CSTRING(" INNER JOIN ") + indexTable +
                             NS_LITERAL_CSTRING(" ON ") + indexTable +
                             NS_LITERAL_CSTRING(".") + objectDataIdColumn +
                             NS_LITERAL_CSTRING(" = ") + objectTable +
                             NS_LITERAL_CSTRING(".id WHERE ") + indexTable +
                             NS_LITERAL_CSTRING(".index_id = :id");

  NS_NAMED_LITERAL_CSTRING(currentKey, "current_key");
  NS_NAMED_LITERAL_CSTRING(rangeKey, "range_key");
  NS_NAMED_LITERAL_CSTRING(objectKey, "object_key");

  switch (mDirection) {
    case nsIIDBCursor::NEXT:
      if (!mUpperKey.IsUnset()) {
        AppendConditionClause(value, rangeKey, true, !mUpperOpen, queryStart);
        mRangeKey = mUpperKey;
      }
      mContinueQuery = queryStart + NS_LITERAL_CSTRING(" AND ( ( ") +
                       value + NS_LITERAL_CSTRING(" = :") + currentKey +
                       NS_LITERAL_CSTRING(" AND ") + keyValue +
                       NS_LITERAL_CSTRING(" > :") + objectKey +
                       NS_LITERAL_CSTRING(" ) OR ( ") + value +
                       NS_LITERAL_CSTRING(" > :") + currentKey +
                       NS_LITERAL_CSTRING(" ) )") + directionClause +
                       NS_LITERAL_CSTRING(" LIMIT 1");
      mContinueToQuery = queryStart + NS_LITERAL_CSTRING(" AND ") + value +
                         NS_LITERAL_CSTRING(" >= :") +
                         currentKey + directionClause +
                         NS_LITERAL_CSTRING(" LIMIT 1");
      break;

    case nsIIDBCursor::NEXT_NO_DUPLICATE:
      if (!mUpperKey.IsUnset()) {
        AppendConditionClause(value, rangeKey, true, !mUpperOpen, queryStart);
        mRangeKey = mUpperKey;
      }
      mContinueQuery = queryStart + NS_LITERAL_CSTRING(" AND ") + value +
                       NS_LITERAL_CSTRING(" > :") + currentKey +
                       directionClause + NS_LITERAL_CSTRING(" LIMIT 1");
      mContinueToQuery = queryStart + NS_LITERAL_CSTRING(" AND ") + value +
                         NS_LITERAL_CSTRING(" >= :") + currentKey +
                         directionClause + NS_LITERAL_CSTRING(" LIMIT 1");
      break;

    case nsIIDBCursor::PREV:
      if (!mLowerKey.IsUnset()) {
        AppendConditionClause(value, rangeKey, false, !mLowerOpen, queryStart);
        mRangeKey = mLowerKey;
      }
      mContinueQuery = queryStart + NS_LITERAL_CSTRING(" AND ( ( ") +
                       value + NS_LITERAL_CSTRING(" = :") + currentKey +
                       NS_LITERAL_CSTRING(" AND ") + keyValue +
                       NS_LITERAL_CSTRING(" < :") + objectKey +
                       NS_LITERAL_CSTRING(" ) OR ( ") + value +
                       NS_LITERAL_CSTRING(" < :") + currentKey +
                       NS_LITERAL_CSTRING(" ) )") + directionClause +
                       NS_LITERAL_CSTRING(" LIMIT 1");
      mContinueToQuery = queryStart + NS_LITERAL_CSTRING(" AND ") + value +
                         NS_LITERAL_CSTRING(" <= :") +
                         currentKey + directionClause +
                         NS_LITERAL_CSTRING(" LIMIT 1");
      break;

    case nsIIDBCursor::PREV_NO_DUPLICATE:
      if (!mLowerKey.IsUnset()) {
        AppendConditionClause(value, rangeKey, false, !mLowerOpen, queryStart);
        mRangeKey = mLowerKey;
      }
      mContinueQuery = queryStart + NS_LITERAL_CSTRING(" AND ") + value +
                       NS_LITERAL_CSTRING(" < :") + currentKey +
                       directionClause + NS_LITERAL_CSTRING(" LIMIT 1");
      mContinueToQuery = queryStart + NS_LITERAL_CSTRING(" AND ") + value +
                         NS_LITERAL_CSTRING(" <= :") + currentKey +
                         directionClause + NS_LITERAL_CSTRING(" LIMIT 1");
      break;

    default:
      NS_NOTREACHED("Unknown direction type!");
  }

  return NS_OK;
}
Beispiel #11
0
void
test_UTFStrings()
{
    nsCOMPtr<mozIStorageConnection> db(getMemoryDatabase());

    // Create table with a single string column.
    (void)db->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
                                   "CREATE TABLE test (str STRING)"
                               ));

    // Create statements to INSERT and SELECT the string.
    nsCOMPtr<mozIStorageStatement> insert, select;
    (void)db->CreateStatement(NS_LITERAL_CSTRING(
                                  "INSERT INTO test (str) VALUES (?1)"
                              ), getter_AddRefs(insert));
    (void)db->CreateStatement(NS_LITERAL_CSTRING(
                                  "SELECT str FROM test"
                              ), getter_AddRefs(select));

    // Roundtrip a UTF8 string through the table, using UTF8 input and output.
    static const char sCharArray[] =
        "I'm a \xc3\xbb\xc3\xbc\xc3\xa2\xc3\xa4\xc3\xa7 UTF8 string!";
    nsCAutoString insertedUTF8(sCharArray, NS_ARRAY_LENGTH(sCharArray) - 1);
    do_check_true(insertedUTF8.Length() == NS_ARRAY_LENGTH(sCharArray) - 1);
    NS_ConvertUTF8toUTF16 insertedUTF16(insertedUTF8);
    do_check_true(insertedUTF8 == NS_ConvertUTF16toUTF8(insertedUTF16));
    {
        mozStorageStatementScoper scoper(insert);
        bool hasResult;
        do_check_true(NS_SUCCEEDED(insert->BindUTF8StringByIndex(0, insertedUTF8)));
        do_check_true(NS_SUCCEEDED(insert->ExecuteStep(&hasResult)));
        do_check_false(hasResult);
    }

    {
        nsCAutoString result;

        mozStorageStatementScoper scoper(select);
        bool hasResult;
        do_check_true(NS_SUCCEEDED(select->ExecuteStep(&hasResult)));
        do_check_true(hasResult);
        do_check_true(NS_SUCCEEDED(select->GetUTF8String(0, result)));

        do_check_true(result == insertedUTF8);
    }

    // Use UTF8 input and UTF16 output.
    {
        nsAutoString result;

        mozStorageStatementScoper scoper(select);
        bool hasResult;
        do_check_true(NS_SUCCEEDED(select->ExecuteStep(&hasResult)));
        do_check_true(hasResult);
        do_check_true(NS_SUCCEEDED(select->GetString(0, result)));

        do_check_true(result == insertedUTF16);
    }

    (void)db->ExecuteSimpleSQL(NS_LITERAL_CSTRING("DELETE FROM test"));

    // Roundtrip the same string using UTF16 input and UTF8 output.
    {
        mozStorageStatementScoper scoper(insert);
        bool hasResult;
        do_check_true(NS_SUCCEEDED(insert->BindStringByIndex(0, insertedUTF16)));
        do_check_true(NS_SUCCEEDED(insert->ExecuteStep(&hasResult)));
        do_check_false(hasResult);
    }

    {
        nsCAutoString result;

        mozStorageStatementScoper scoper(select);
        bool hasResult;
        do_check_true(NS_SUCCEEDED(select->ExecuteStep(&hasResult)));
        do_check_true(hasResult);
        do_check_true(NS_SUCCEEDED(select->GetUTF8String(0, result)));

        do_check_true(result == insertedUTF8);
    }

    // Use UTF16 input and UTF16 output.
    {
        nsAutoString result;

        mozStorageStatementScoper scoper(select);
        bool hasResult;
        do_check_true(NS_SUCCEEDED(select->ExecuteStep(&hasResult)));
        do_check_true(hasResult);
        do_check_true(NS_SUCCEEDED(select->GetString(0, result)));

        do_check_true(result == insertedUTF16);
    }

    (void)db->ExecuteSimpleSQL(NS_LITERAL_CSTRING("DELETE FROM test"));
}