tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit e236d89e70544d8d4755c46b4a407b3ca599a030
parent 82e2435fd101e64854b8c56ac792c8a5522db4fe
Author: Daisuke Akatsuka <daisuke@birchill.co.jp>
Date:   Fri, 21 Nov 2025 01:18:52 +0000

Bug 1846461: Move logic that appends children of folder to nsNavHistoryResult r=places-reviewers,mak

Differential Revision: https://phabricator.services.mozilla.com/D273158

Diffstat:
Mtoolkit/components/places/nsNavBookmarks.cpp | 176-------------------------------------------------------------------------------
Mtoolkit/components/places/nsNavBookmarks.h | 40----------------------------------------
Mtoolkit/components/places/nsNavHistoryResult.cpp | 198+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------
Mtoolkit/components/places/nsNavHistoryResult.h | 23+++++++++++++++++++++++
4 files changed, 191 insertions(+), 246 deletions(-)

diff --git a/toolkit/components/places/nsNavBookmarks.cpp b/toolkit/components/places/nsNavBookmarks.cpp @@ -1504,182 +1504,6 @@ nsNavBookmarks::GetItemTitle(int64_t aItemId, nsACString& _title) { return NS_OK; } -nsresult nsNavBookmarks::QueryFolderChildren( - int64_t aFolderId, nsNavHistoryQueryOptions* aOptions, - nsCOMArray<nsNavHistoryResultNode>* aChildren) { - NS_ENSURE_ARG_POINTER(aOptions); - NS_ENSURE_ARG_POINTER(aChildren); - - // Select all children of a given folder, sorted by position. - // This is a LEFT JOIN because not all bookmarks types have a place. - // We construct a result where the first columns exactly match those returned - // by mDBGetURLPageInfo, and additionally contains columns for position, - // item_child, and folder_child from moz_bookmarks. - nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement( - nsNavHistory::GetTagsSqlFragment( - nsINavHistoryQueryOptions::QUERY_TYPE_BOOKMARKS, - aOptions->ExcludeItems()) + - "SELECT " - " h.id, h.url, b.title, h.rev_host, h.visit_count, " - " h.last_visit_date, null, b.id, b.dateAdded, b.lastModified, b.parent, " - " (SELECT tags FROM tagged WHERE place_id = h.id) AS tags, " - " h.frecency, h.hidden, h.guid, null, null, null, " - " b.guid, b.position, b.type, b.fk, t.guid, t.id, t.title " - "FROM moz_bookmarks b " - "LEFT JOIN moz_places h ON b.fk = h.id " - "LEFT JOIN moz_bookmarks t ON t.guid = target_folder_guid(h.url) " - "WHERE b.parent = :parent " - "AND (NOT :excludeItems OR " - "b.type = :folder OR " - "h.url_hash BETWEEN hash('place', 'prefix_lo') " - " AND hash('place', 'prefix_hi')) " - "ORDER BY b.position ASC"_ns); - NS_ENSURE_STATE(stmt); - mozStorageStatementScoper scoper(stmt); - - nsresult rv = stmt->BindInt64ByName("parent"_ns, aFolderId); - NS_ENSURE_SUCCESS(rv, rv); - rv = stmt->BindInt32ByName("folder"_ns, TYPE_FOLDER); - NS_ENSURE_SUCCESS(rv, rv); - rv = stmt->BindInt32ByName("excludeItems"_ns, aOptions->ExcludeItems()); - NS_ENSURE_SUCCESS(rv, rv); - - nsCOMPtr<mozIStorageValueArray> row = do_QueryInterface(stmt, &rv); - NS_ENSURE_SUCCESS(rv, rv); - - int32_t index = -1; - bool hasResult; - while (NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult) { - rv = ProcessFolderNodeRow(row, aOptions, aChildren, index); - NS_ENSURE_SUCCESS(rv, rv); - } - - return NS_OK; -} - -nsresult nsNavBookmarks::ProcessFolderNodeRow( - mozIStorageValueArray* aRow, nsNavHistoryQueryOptions* aOptions, - nsCOMArray<nsNavHistoryResultNode>* aChildren, int32_t& aCurrentIndex) { - NS_ENSURE_ARG_POINTER(aRow); - NS_ENSURE_ARG_POINTER(aOptions); - NS_ENSURE_ARG_POINTER(aChildren); - - // The results will be in order of aCurrentIndex. Even if we don't add a node - // because it was excluded, we need to count its index, so do that before - // doing anything else. - aCurrentIndex++; - - int32_t itemType; - nsresult rv = aRow->GetInt32(kGetChildrenIndex_Type, &itemType); - NS_ENSURE_SUCCESS(rv, rv); - int64_t id; - rv = aRow->GetInt64(nsNavHistory::kGetInfoIndex_ItemId, &id); - NS_ENSURE_SUCCESS(rv, rv); - - RefPtr<nsNavHistoryResultNode> node; - - if (itemType == TYPE_BOOKMARK) { - nsNavHistory* history = nsNavHistory::GetHistoryService(); - NS_ENSURE_TRUE(history, NS_ERROR_OUT_OF_MEMORY); - rv = history->RowToResult(aRow, aOptions, getter_AddRefs(node)); - NS_ENSURE_SUCCESS(rv, rv); - uint32_t nodeType; - node->GetType(&nodeType); - if (nodeType == nsINavHistoryResultNode::RESULT_TYPE_QUERY && - aOptions->ExcludeQueries()) { - return NS_OK; - } - } else if (itemType == TYPE_FOLDER) { - nsAutoCString title; - bool isNull; - rv = aRow->GetIsNull(nsNavHistory::kGetInfoIndex_Title, &isNull); - NS_ENSURE_SUCCESS(rv, rv); - if (!isNull) { - rv = aRow->GetUTF8String(nsNavHistory::kGetInfoIndex_Title, title); - NS_ENSURE_SUCCESS(rv, rv); - } - - nsAutoCString guid; - rv = aRow->GetUTF8String(kGetChildrenIndex_Guid, guid); - NS_ENSURE_SUCCESS(rv, rv); - - // Don't use options from the parent to build the new folder node, it will - // inherit those later when it's inserted in the result. - node = new nsNavHistoryFolderResultNode(id, guid, id, guid, title, - new nsNavHistoryQueryOptions()); - - rv = aRow->GetInt64(nsNavHistory::kGetInfoIndex_ItemDateAdded, - reinterpret_cast<int64_t*>(&node->mDateAdded)); - NS_ENSURE_SUCCESS(rv, rv); - rv = aRow->GetInt64(nsNavHistory::kGetInfoIndex_ItemLastModified, - reinterpret_cast<int64_t*>(&node->mLastModified)); - NS_ENSURE_SUCCESS(rv, rv); - } else { - // This is a separator. - node = new nsNavHistorySeparatorResultNode(); - - node->mItemId = id; - rv = aRow->GetUTF8String(kGetChildrenIndex_Guid, node->mBookmarkGuid); - NS_ENSURE_SUCCESS(rv, rv); - rv = aRow->GetInt64(nsNavHistory::kGetInfoIndex_ItemDateAdded, - reinterpret_cast<int64_t*>(&node->mDateAdded)); - NS_ENSURE_SUCCESS(rv, rv); - rv = aRow->GetInt64(nsNavHistory::kGetInfoIndex_ItemLastModified, - reinterpret_cast<int64_t*>(&node->mLastModified)); - NS_ENSURE_SUCCESS(rv, rv); - } - - // Store the index of the node within this container. Note that this is not - // moz_bookmarks.position. - node->mBookmarkIndex = aCurrentIndex; - - NS_ENSURE_TRUE(aChildren->AppendObject(node), NS_ERROR_OUT_OF_MEMORY); - return NS_OK; -} - -nsresult nsNavBookmarks::QueryFolderChildrenAsync( - nsNavHistoryFolderResultNode* aNode, - mozIStoragePendingStatement** _pendingStmt) { - NS_ENSURE_ARG_POINTER(aNode); - NS_ENSURE_ARG_POINTER(_pendingStmt); - - // Select all children of a given folder, sorted by position. - // This is a LEFT JOIN because not all bookmarks types have a place. - // We construct a result where the first columns exactly match those returned - // by mDBGetURLPageInfo, and additionally contains columns for position, - // item_child, and folder_child from moz_bookmarks. - nsCOMPtr<mozIStorageAsyncStatement> stmt = mDB->GetAsyncStatement( - "SELECT h.id, h.url, b.title, h.rev_host, h.visit_count, " - "h.last_visit_date, null, b.id, b.dateAdded, b.lastModified, " - "b.parent, null, h.frecency, h.hidden, h.guid, null, null, null, " - "b.guid, b.position, b.type, b.fk, t.guid, t.id, t.title " - "FROM moz_bookmarks b " - "LEFT JOIN moz_places h ON b.fk = h.id " - "LEFT JOIN moz_bookmarks t ON t.guid = target_folder_guid(h.url) " - "WHERE b.parent = :parent " - "AND (NOT :excludeItems OR " - "b.type = :folder OR " - "h.url_hash BETWEEN hash('place', 'prefix_lo') AND hash('place', " - "'prefix_hi')) " - "ORDER BY b.position ASC"); - NS_ENSURE_STATE(stmt); - - nsresult rv = stmt->BindInt64ByName("parent"_ns, aNode->mTargetFolderItemId); - NS_ENSURE_SUCCESS(rv, rv); - rv = stmt->BindInt32ByName("folder"_ns, TYPE_FOLDER); - NS_ENSURE_SUCCESS(rv, rv); - rv = - stmt->BindInt32ByName("excludeItems"_ns, aNode->mOptions->ExcludeItems()); - NS_ENSURE_SUCCESS(rv, rv); - - nsCOMPtr<mozIStoragePendingStatement> pendingStmt; - rv = stmt->ExecuteAsync(aNode, getter_AddRefs(pendingStmt)); - NS_ENSURE_SUCCESS(rv, rv); - - NS_IF_ADDREF(*_pendingStmt = pendingStmt); - return NS_OK; -} - nsresult nsNavBookmarks::FetchFolderInfo(int64_t aFolderId, int32_t* _folderCount, nsACString& _guid, diff --git a/toolkit/components/places/nsNavBookmarks.h b/toolkit/components/places/nsNavBookmarks.h @@ -101,46 +101,6 @@ class nsNavBookmarks final : public nsINavBookmarksService, bool aHidden, uint32_t aVisitCount, uint32_t aTyped, const nsAString& aLastKnownTitle); - // Find all the children of a folder, using the given query and options. - // For each child, a ResultNode is created and added to |children|. - // The results are ordered by folder position. - nsresult QueryFolderChildren(int64_t aFolderId, - nsNavHistoryQueryOptions* aOptions, - nsCOMArray<nsNavHistoryResultNode>* children); - - /** - * Turns aRow into a node and appends it to aChildren if it is appropriate to - * do so. - * - * @param aRow - * A Storage statement (in the case of synchronous execution) or row of - * a result set (in the case of asynchronous execution). - * @param aOptions - * The options of the parent folder node. These are the options used - * to fill the parent node. - * @param aChildren - * The children of the parent folder node. - * @param aCurrentIndex - * The index of aRow within the results. When called on the first row, - * this should be set to -1. - */ - nsresult ProcessFolderNodeRow(mozIStorageValueArray* aRow, - nsNavHistoryQueryOptions* aOptions, - nsCOMArray<nsNavHistoryResultNode>* aChildren, - int32_t& aCurrentIndex); - - /** - * The async version of QueryFolderChildren. - * - * @param aNode - * The folder node that will receive the children. - * @param _pendingStmt - * The Storage pending statement that will be used to control async - * execution. - */ - nsresult QueryFolderChildrenAsync(nsNavHistoryFolderResultNode* aNode, - mozIStoragePendingStatement** _pendingStmt); - /** * Fetches information about the specified id from the database. * diff --git a/toolkit/components/places/nsNavHistoryResult.cpp b/toolkit/components/places/nsNavHistoryResult.cpp @@ -2689,17 +2689,7 @@ nsNavHistoryFolderResultNode::GetQueryOptions( } nsresult nsNavHistoryFolderResultNode::FillChildren() { - NS_ASSERTION(!mContentsValid, - "Don't call FillChildren when contents are valid"); - NS_ASSERTION(mChildren.Count() == 0, - "We are trying to fill children when there already are some"); - - nsNavBookmarks* bookmarks = nsNavBookmarks::GetBookmarksService(); - NS_ENSURE_TRUE(bookmarks, NS_ERROR_OUT_OF_MEMORY); - - // Actually get the folder children from the bookmark service. - nsresult rv = - bookmarks->QueryFolderChildren(mTargetFolderItemId, mOptions, &mChildren); + nsresult rv = FillChildrenInternal(); NS_ENSURE_SUCCESS(rv, rv); // PERFORMANCE: it may be better to also fill any child folders at this point @@ -2761,23 +2751,16 @@ void nsNavHistoryFolderResultNode::EnsureRegisteredAsFolderObserver() { } /** - * The async version of FillChildren. This begins asynchronous execution by - * calling nsNavBookmarks::QueryFolderChildrenAsync. During execution, this - * node's async Storage callbacks, HandleResult and HandleCompletion, will be - * called. + * The async version of FillChildren. This begins asynchronous execution by + * calling FillChildrenInternal. During execution, this node's async Storage + * callbacks, HandleResult and HandleCompletion, will be called. */ nsresult nsNavHistoryFolderResultNode::FillChildrenAsync() { - NS_ASSERTION(!mContentsValid, "FillChildrenAsync when contents are valid"); - NS_ASSERTION(mChildren.Count() == 0, "FillChildrenAsync when children exist"); - // ProcessFolderNodeChild, called in HandleResult, increments this for every // result row it processes. Initialize it here as we begin async execution. mAsyncBookmarkIndex = -1; - nsNavBookmarks* bmSvc = nsNavBookmarks::GetBookmarksService(); - NS_ENSURE_TRUE(bmSvc, NS_ERROR_OUT_OF_MEMORY); - nsresult rv = - bmSvc->QueryFolderChildrenAsync(this, getter_AddRefs(mAsyncPendingStmt)); + nsresult rv = FillChildrenInternal(getter_AddRefs(mAsyncPendingStmt)); NS_ENSURE_SUCCESS(rv, rv); // Register with the result for updates. All updates during async execution @@ -2787,6 +2770,168 @@ nsresult nsNavHistoryFolderResultNode::FillChildrenAsync() { return NS_OK; } +nsresult nsNavHistoryFolderResultNode::FillChildrenInternal( + mozIStoragePendingStatement** aPendingStmt) { + NS_ASSERTION(!mContentsValid, + "Don't call FillChildrenInternal when contents are valid"); + NS_ASSERTION(mChildren.Count() == 0, + "We are trying to fill children when there already are some"); + + // Select all children of a given folder, sorted by position. + // This is a LEFT JOIN because not all bookmarks types have a place. + // We construct a result where the first columns exactly match those returned + // by mDBGetURLPageInfo, and additionally contains columns for position, + // item_child, and folder_child from moz_bookmarks. + bool isAsync = !!aPendingStmt; + nsCString sql = + "SELECT " + " h.id, h.url, b.title, h.rev_host, h.visit_count, h.last_visit_date, " + " null, b.id, b.dateAdded, b.lastModified, b.parent, "_ns + + nsCString( + isAsync + ? " null, " + : " (SELECT tags FROM tagged WHERE place_id = h.id) AS tags, ") + + " h.frecency, h.hidden, h.guid, null, null, null, b.guid, b.position, " + " b.type, b.fk, t.guid, t.id, t.title " + "FROM moz_bookmarks b " + "LEFT JOIN moz_places h ON b.fk = h.id " + "LEFT JOIN moz_bookmarks t ON t.guid = target_folder_guid(h.url) " + "WHERE b.parent = :parent " + " AND (" + " NOT :excludeItems OR " + " b.type = :folder OR " + " h.url_hash BETWEEN " + " hash('place', 'prefix_lo') AND hash('place', 'prefix_hi')" + " ) " + "ORDER BY b.position ASC"_ns; + + RefPtr<Database> db = Database::GetDatabase(); + NS_ENSURE_STATE(db); + nsCOMPtr<mozIStorageBaseStatement> stmt; + if (isAsync) { + stmt = db->GetAsyncStatement(sql); + NS_ENSURE_STATE(stmt); + } else { + stmt = db->GetStatement(nsNavHistory::GetTagsSqlFragment( + nsINavHistoryQueryOptions::QUERY_TYPE_BOOKMARKS, + mOptions->ExcludeItems()) + + sql); + NS_ENSURE_STATE(stmt); + } + + nsresult rv = stmt->BindInt64ByName("parent"_ns, mTargetFolderItemId); + NS_ENSURE_SUCCESS(rv, rv); + rv = stmt->BindInt32ByName("folder"_ns, nsINavBookmarksService::TYPE_FOLDER); + NS_ENSURE_SUCCESS(rv, rv); + rv = stmt->BindInt32ByName("excludeItems"_ns, mOptions->ExcludeItems()); + NS_ENSURE_SUCCESS(rv, rv); + + if (isAsync) { + nsCOMPtr<mozIStorageAsyncStatement> async = do_QueryInterface(stmt, &rv); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr<mozIStoragePendingStatement> pendingStmt; + rv = async->ExecuteAsync(this, getter_AddRefs(pendingStmt)); + NS_ENSURE_SUCCESS(rv, rv); + + NS_IF_ADDREF(*aPendingStmt = pendingStmt); + } else { + nsCOMPtr<mozIStorageStatement> sync = do_QueryInterface(stmt, &rv); + NS_ENSURE_SUCCESS(rv, rv); + mozStorageStatementScoper scoper(sync); + nsCOMPtr<mozIStorageValueArray> row = do_QueryInterface(sync, &rv); + NS_ENSURE_SUCCESS(rv, rv); + + int32_t index = -1; + bool hasResult; + while (NS_SUCCEEDED(sync->ExecuteStep(&hasResult)) && hasResult) { + rv = AppendRowAsChild(row, index); + NS_ENSURE_SUCCESS(rv, rv); + } + } + + return NS_OK; +} + +nsresult nsNavHistoryFolderResultNode::AppendRowAsChild( + mozIStorageValueArray* aRow, int32_t& aCurrentIndex) { + NS_ENSURE_ARG_POINTER(aRow); + + // The results will be in order of aCurrentIndex. Even if we don't add a node + // because it was excluded, we need to count its index, so do that before + // doing anything else. + aCurrentIndex++; + + int32_t itemType; + nsresult rv = + aRow->GetInt32(nsNavBookmarks::kGetChildrenIndex_Type, &itemType); + NS_ENSURE_SUCCESS(rv, rv); + int64_t id; + rv = aRow->GetInt64(nsNavHistory::kGetInfoIndex_ItemId, &id); + NS_ENSURE_SUCCESS(rv, rv); + + RefPtr<nsNavHistoryResultNode> node; + + if (itemType == nsINavBookmarksService::TYPE_BOOKMARK) { + nsNavHistory* history = nsNavHistory::GetHistoryService(); + NS_ENSURE_TRUE(history, NS_ERROR_OUT_OF_MEMORY); + rv = history->RowToResult(aRow, mOptions, getter_AddRefs(node)); + NS_ENSURE_SUCCESS(rv, rv); + uint32_t nodeType; + node->GetType(&nodeType); + if (nodeType == nsINavHistoryResultNode::RESULT_TYPE_QUERY && + mOptions->ExcludeQueries()) { + return NS_OK; + } + } else if (itemType == nsINavBookmarksService::TYPE_FOLDER) { + nsAutoCString title; + bool isNull; + rv = aRow->GetIsNull(nsNavHistory::kGetInfoIndex_Title, &isNull); + NS_ENSURE_SUCCESS(rv, rv); + if (!isNull) { + rv = aRow->GetUTF8String(nsNavHistory::kGetInfoIndex_Title, title); + NS_ENSURE_SUCCESS(rv, rv); + } + + nsAutoCString guid; + rv = aRow->GetUTF8String(nsNavBookmarks::kGetChildrenIndex_Guid, guid); + NS_ENSURE_SUCCESS(rv, rv); + + // Don't use options from the parent to build the new folder node, it will + // inherit those later when it's inserted in the result. + node = new nsNavHistoryFolderResultNode(id, guid, id, guid, title, + new nsNavHistoryQueryOptions()); + + rv = aRow->GetInt64(nsNavHistory::kGetInfoIndex_ItemDateAdded, + reinterpret_cast<int64_t*>(&node->mDateAdded)); + NS_ENSURE_SUCCESS(rv, rv); + rv = aRow->GetInt64(nsNavHistory::kGetInfoIndex_ItemLastModified, + reinterpret_cast<int64_t*>(&node->mLastModified)); + NS_ENSURE_SUCCESS(rv, rv); + } else { + // This is a separator. + node = new nsNavHistorySeparatorResultNode(); + + node->mItemId = id; + rv = aRow->GetUTF8String(nsNavBookmarks::kGetChildrenIndex_Guid, + node->mBookmarkGuid); + NS_ENSURE_SUCCESS(rv, rv); + rv = aRow->GetInt64(nsNavHistory::kGetInfoIndex_ItemDateAdded, + reinterpret_cast<int64_t*>(&node->mDateAdded)); + NS_ENSURE_SUCCESS(rv, rv); + rv = aRow->GetInt64(nsNavHistory::kGetInfoIndex_ItemLastModified, + reinterpret_cast<int64_t*>(&node->mLastModified)); + NS_ENSURE_SUCCESS(rv, rv); + } + + // Store the index of the node within this container. Note that this is not + // moz_bookmarks.position. + node->mBookmarkIndex = aCurrentIndex; + + NS_ENSURE_TRUE(mChildren.AppendObject(node), NS_ERROR_OUT_OF_MEMORY); + return NS_OK; +} + /** * A mozIStorageStatementCallback method. Called during the async execution * begun by FillChildrenAsync. @@ -2798,17 +2943,10 @@ NS_IMETHODIMP nsNavHistoryFolderResultNode::HandleResult(mozIStorageResultSet* aResultSet) { NS_ENSURE_ARG_POINTER(aResultSet); - nsNavBookmarks* bmSvc = nsNavBookmarks::GetBookmarksService(); - if (!bmSvc) { - CancelAsyncOpen(false); - return NS_ERROR_OUT_OF_MEMORY; - } - // Consume all the currently available rows of the result set. nsCOMPtr<mozIStorageRow> row; while (NS_SUCCEEDED(aResultSet->GetNextRow(getter_AddRefs(row))) && row) { - nsresult rv = bmSvc->ProcessFolderNodeRow(row, mOptions, &mChildren, - mAsyncBookmarkIndex); + nsresult rv = AppendRowAsChild(row, mAsyncBookmarkIndex); if (NS_FAILED(rv)) { CancelAsyncOpen(false); return rv; diff --git a/toolkit/components/places/nsNavHistoryResult.h b/toolkit/components/places/nsNavHistoryResult.h @@ -797,6 +797,29 @@ class nsNavHistoryFolderResultNode final nsresult OnChildrenFilled(); void EnsureRegisteredAsFolderObserver(); nsresult FillChildrenAsync(); + /* + * Fill children of this folder by the current query. + * + * @param aPendingStmt + * The Storage pending statement that will be used to control async + * execution. If this is nullptr, this method processes as sync. + */ + nsresult FillChildrenInternal( + mozIStoragePendingStatement** aPendingStmt = nullptr); + + /** + * Turns aRow into a node and appends it as a child of this node if it is + * appropriate to do so. + * + * @param aRow + * A Storage statement (in the case of synchronous execution) or row of + * a result set (in the case of asynchronous execution). + * @param aCurrentIndex + * The index of aRow within the results. When called on the first row, + * this should be set to -1. + */ + nsresult AppendRowAsChild(mozIStorageValueArray* aRow, + int32_t& aCurrentIndex); bool mIsRegisteredFolderObserver; int32_t mAsyncBookmarkIndex;