From: csagan5 <32685696+csagan5@users.noreply.github.com> Date: Thu, 18 Feb 2021 21:22:52 +0100 Subject: Add menu item to bookmark all tabs License: GPL-3.0-only - https://spdx.org/licenses/GPL-3.0-only.html --- chrome/android/java/res/menu/main_menu.xml | 7 ++ .../chrome/browser/ChromeTabbedActivity.java | 24 +++++++ .../browser/bookmarks/BookmarkBridge.java | 64 +++++++++++++++++++ .../bookmarks/android/bookmark_bridge.cc | 43 +++++++++++++ .../bookmarks/android/bookmark_bridge.h | 8 +++ .../browser/bookmarks/bookmark_html_writer.cc | 8 ++- .../bookmarks/chrome_bookmark_client.cc | 2 + .../dialogs/DownloadLocationCustomView.java | 4 +- .../strings/android_chrome_strings.grd | 3 + components/bookmark_bar_strings.grdp | 6 ++ .../bookmarks/browser/bookmark_codec.cc | 22 +++++-- components/bookmarks/browser/bookmark_codec.h | 7 +- .../browser/bookmark_load_details.cc | 4 ++ .../bookmarks/browser/bookmark_load_details.h | 3 + .../bookmarks/browser/bookmark_model.cc | 3 +- components/bookmarks/browser/bookmark_model.h | 7 ++ components/bookmarks/browser/bookmark_node.cc | 11 ++++ components/bookmarks/browser/bookmark_node.h | 4 ++ .../bookmarks/browser/bookmark_uuids.cc | 3 + components/bookmarks/browser/bookmark_uuids.h | 1 + components/bookmarks/browser/model_loader.cc | 3 +- .../bookmark_specifics_conversions.cc | 1 + 22 files changed, 228 insertions(+), 10 deletions(-) diff --git a/chrome/android/java/res/menu/main_menu.xml b/chrome/android/java/res/menu/main_menu.xml --- a/chrome/android/java/res/menu/main_menu.xml +++ b/chrome/android/java/res/menu/main_menu.xml @@ -71,6 +71,10 @@ found in the LICENSE file. + @@ -183,6 +187,9 @@ found in the LICENSE file. + diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java --- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java @@ -84,6 +84,7 @@ import org.chromium.chrome.browser.back_press.MinimizeAppAndCloseTabBackPressHan import org.chromium.chrome.browser.bookmarks.BookmarkUtils; import org.chromium.chrome.browser.browserservices.intents.WebappConstants; import org.chromium.chrome.browser.compositor.CompositorViewHolder; +import org.chromium.chrome.browser.bookmarks.BookmarkModel; import org.chromium.chrome.browser.compositor.layouts.Layout; import org.chromium.chrome.browser.compositor.layouts.LayoutManagerChrome; import org.chromium.chrome.browser.compositor.layouts.LayoutManagerChromePhone; @@ -2294,6 +2295,8 @@ public class ChromeTabbedActivity extends ChromeActivity getTabModelSelector().closeAllTabs()); RecordUserAction.record("MobileMenuCloseAllTabs"); + } else if (id == R.id.bookmark_all_tabs_menu_id) { + bookmarkAllTabs(); } else if (id == R.id.close_all_incognito_tabs_menu_id) { // Close only incognito tabs CloseAllTabsDialog.show(this, getModalDialogManagerSupplier(), @@ -2359,6 +2362,27 @@ public class ChromeTabbedActivity extends ChromeActivity { + for (int i = 0; i < tabModel.getCount(); i++) { + Tab tab = tabModel.getTabAt(i); + if (tab.isNativePage()) { + continue; + } + bookmarkModel.addToTabsCollection(this, tab); + } + bookmarkModel.finishedAddingToTabsCollection(this, getSnackbarManager()); + }); + } + private void recordLauncherShortcutAction(boolean isIncognito) { if (isIncognito) { RecordUserAction.record("Android.LauncherShortcut.NewIncognitoTab"); diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkBridge.java --- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkBridge.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkBridge.java @@ -29,6 +29,7 @@ import com.google.protobuf.InvalidProtocolBufferException; import org.chromium.base.Callback; import org.chromium.base.ContextUtils; +import org.chromium.base.Log; import org.chromium.base.ObserverList; import org.chromium.base.ThreadUtils; import org.chromium.base.annotations.CalledByNative; @@ -39,6 +40,10 @@ import org.chromium.chrome.browser.partnerbookmarks.PartnerBookmarksShim; import org.chromium.chrome.browser.profiles.Profile; import org.chromium.chrome.browser.read_later.ReadingListUtils; import org.chromium.chrome.browser.tab.Tab; +import org.chromium.chrome.browser.ui.messages.snackbar.Snackbar; +import org.chromium.chrome.browser.ui.messages.snackbar.SnackbarManager; +import org.chromium.chrome.browser.ui.messages.snackbar.SnackbarManager.SnackbarController; +import org.chromium.chrome.R; import org.chromium.components.bookmarks.BookmarkId; import org.chromium.components.bookmarks.BookmarkItem; import org.chromium.components.bookmarks.BookmarkType; @@ -83,6 +88,8 @@ import java.io.File; class BookmarkBridge { private final ObserverList mObservers = new ObserverList<>(); + private static final String TAG = "BookmarkBridge"; + private long mNativeBookmarkBridge; private boolean mIsDestroyed; private boolean mIsDoingExtensiveChanges; @@ -375,6 +382,16 @@ class BookmarkBridge { return mMobileFolderId; } + /** + * @return The BookmarkId for the Tabs collecction folder node + */ + public BookmarkId getTabsCollectionFolderId() { + ThreadUtils.assertOnUiThread(); + assert mIsNativeBookmarkModelLoaded; + return BookmarkBridgeJni.get().getTabsCollectionFolderId( + mNativeBookmarkBridge, BookmarkBridge.this); + } + /** * @return Id representing the special "other" folder from bookmark model. */ @@ -1008,6 +1025,50 @@ class BookmarkBridge { return BookmarkBridgeJni.get().addToReadingList(mNativeBookmarkBridge, title, url); } + // Used to bookmark all tabs in a specific folder, created if not existing + public BookmarkId addToTabsCollection(Context context, Tab tab) { + BookmarkId parent = getTabsCollectionFolderId(); + BookmarkId existingId = BookmarkBridgeJni.get().getBookmarkIdForTabsCollection( + mNativeBookmarkBridge, tab.getOriginalUrl()); + if (existingId != null && existingId.getId() != BookmarkId.INVALID_ID) { + BookmarkId existingBookmarkId = new BookmarkId(existingId.getId(), BookmarkType.NORMAL); + BookmarkItem existingBookmark = getBookmarkById(existingBookmarkId); + if (parent.equals(existingBookmark.getParentId())) { + // bookmark already exists in the tabs collection folder + return existingBookmarkId; + } + } + BookmarkId bookmarkId = + addBookmark(parent, getChildCount(parent), tab.getTitle(), tab.getUrl()); + + if (bookmarkId == null) { + Log.e(TAG, + "Failed to add bookmarks: parentTypeAndId %s", parent); + } + return bookmarkId; + } + + public void finishedAddingToTabsCollection(Activity activity, SnackbarManager snackbarManager) { + BookmarkId parent = getTabsCollectionFolderId(); + + BookmarkItem bookmarkItem = getBookmarkById(parent); + String folderName; + if (bookmarkItem != null) { + folderName = bookmarkItem.getTitle(); + } else { + folderName = ""; + } + SnackbarController snackbarController = new SnackbarController() { + @Override + public void onAction(Object actionData) { + } + }; + Snackbar snackbar = Snackbar.make(folderName, snackbarController, Snackbar.TYPE_ACTION, + Snackbar.UMA_BOOKMARK_ADDED) + .setTemplateText(activity.getString(R.string.bookmark_page_saved_folder)); + snackbarManager.showSnackbar(snackbar); + } + /** * @param url The URL of the reading list item. * @return The reading list item with the URL, or null if no such reading list item. @@ -1221,12 +1282,15 @@ class BookmarkBridge { void getImageUrlForBookmark(long nativeBookmarkBridge, GURL url, Callback callback); BookmarkId getBookmarkIdForWebContents( long nativeBookmarkBridge, WebContents webContents, boolean onlyEditable); + BookmarkId getBookmarkIdForTabsCollection( + long nativeBookmarkBridge, GURL url); BookmarkItem getBookmarkById(long nativeBookmarkBridge, long id, int type); void getTopLevelFolderIds(long nativeBookmarkBridge, List bookmarksList); BookmarkId getReadingListFolder(long nativeBookmarkBridge); void getAllFoldersWithDepths( long nativeBookmarkBridge, List folderList, List depthList); BookmarkId getRootFolderId(long nativeBookmarkBridge); + BookmarkId getTabsCollectionFolderId(long nativeBookmarkBridge, BookmarkBridge caller); BookmarkId getMobileFolderId(long nativeBookmarkBridge); BookmarkId getOtherFolderId(long nativeBookmarkBridge); BookmarkId getDesktopFolderId(long nativeBookmarkBridge); diff --git a/chrome/browser/bookmarks/android/bookmark_bridge.cc b/chrome/browser/bookmarks/android/bookmark_bridge.cc --- a/chrome/browser/bookmarks/android/bookmark_bridge.cc +++ b/chrome/browser/bookmarks/android/bookmark_bridge.cc @@ -330,6 +330,33 @@ void BookmarkBridge::GetImageUrlForBookmark( base::BindOnce(&HandleImageUrlResponse, callback)); } +base::android::ScopedJavaLocalRef +BookmarkBridge::GetBookmarkIdForTabsCollection( + JNIEnv* env, + const JavaParamRef& url) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + + GURL gurl = *url::GURLAndroid::ToNativeGURL(env, url); + + bookmarks::BookmarkModel* model = + BookmarkModelFactory::GetForBrowserContext(profile_); + + std::vector nodes = + model->GetNodesByURL(gurl); + std::sort(nodes.begin(), nodes.end(), &bookmarks::MoreRecentlyAdded); + + for (const auto* node : nodes) { + for (const auto& child : model->tabs_collection_node()->children()) { + if (node->id() == child->id()) { + return JavaBookmarkIdCreateBookmarkId(env, node->id(), + GetBookmarkType(node)); + } + } + } + + return nullptr; +} + base::android::ScopedJavaLocalRef BookmarkBridge::GetBookmarkIdForWebContents( JNIEnv* env, @@ -436,6 +463,10 @@ void BookmarkBridge::GetTopLevelFolderIds( } top_level_folders.push_back(root_child.get()); } + for (const auto& node : bookmark_model_->tabs_collection_node()->children()) { + if (node->is_folder()) + top_level_folders.push_back(node.get()); + } if (managed_bookmark_service_->managed_node() && !managed_bookmark_service_->managed_node()->children().empty()) { @@ -479,6 +510,7 @@ void BookmarkBridge::GetAllFoldersWithDepths( // Vector to temporarily contain all child bookmarks at same level for sorting std::vector bookmarks = { bookmark_model_->mobile_node(), + bookmark_model_->tabs_collection_node(), bookmark_model_->bookmark_bar_node(), bookmark_model_->other_node(), }; @@ -1051,6 +1083,17 @@ void BookmarkBridge::GetBookmarksOfType( } } +ScopedJavaLocalRef BookmarkBridge::GetTabsCollectionFolderId( + JNIEnv* env, + const JavaParamRef& obj) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + const BookmarkNode* tabs_collection_node = bookmark_model_->tabs_collection_node(); + ScopedJavaLocalRef folder_id_obj = + JavaBookmarkIdCreateBookmarkId( + env, tabs_collection_node->id(), GetBookmarkType(tabs_collection_node)); + return folder_id_obj; +} + ScopedJavaLocalRef BookmarkBridge::AddFolder( JNIEnv* env, const JavaParamRef& j_parent_id_obj, diff --git a/chrome/browser/bookmarks/android/bookmark_bridge.h b/chrome/browser/bookmarks/android/bookmark_bridge.h --- a/chrome/browser/bookmarks/android/bookmark_bridge.h +++ b/chrome/browser/bookmarks/android/bookmark_bridge.h @@ -69,6 +69,10 @@ class BookmarkBridge : public bookmarks::BaseBookmarkModelObserver, const base::android::JavaParamRef& j_url, const base::android::JavaParamRef& j_callback); + base::android::ScopedJavaLocalRef GetBookmarkIdForTabsCollection( + JNIEnv* env, + const base::android::JavaParamRef& url); + base::android::ScopedJavaLocalRef GetBookmarkIdForWebContents( JNIEnv* env, @@ -205,6 +209,10 @@ class BookmarkBridge : public bookmarks::BaseBookmarkModelObserver, const base::android::JavaParamRef& j_list, jint type); + base::android::ScopedJavaLocalRef GetTabsCollectionFolderId( + JNIEnv* env, + const base::android::JavaParamRef& obj); + base::android::ScopedJavaLocalRef AddFolder( JNIEnv* env, const base::android::JavaParamRef& j_parent_id_obj, diff --git a/chrome/browser/bookmarks/bookmark_html_writer.cc b/chrome/browser/bookmarks/bookmark_html_writer.cc --- a/chrome/browser/bookmarks/bookmark_html_writer.cc +++ b/chrome/browser/bookmarks/bookmark_html_writer.cc @@ -190,15 +190,19 @@ class Writer : public base::RefCountedThreadSafe { roots->FindDict(BookmarkCodec::kOtherBookmarkFolderNameKey); base::Value::Dict* mobile_folder_value = roots->FindDict(BookmarkCodec::kMobileBookmarkFolderNameKey); + base::Value::Dict* tabs_collection_value = + roots->FindDict(BookmarkCodec::kTabsBookmarkFolderNameKey); DCHECK(root_folder_value); DCHECK(other_folder_value); DCHECK(mobile_folder_value); + DCHECK(tabs_collection_value); IncrementIndent(); if (!WriteNode(*root_folder_value, BookmarkNode::BOOKMARK_BAR) || !WriteNode(*other_folder_value, BookmarkNode::OTHER_NODE) || - !WriteNode(*mobile_folder_value, BookmarkNode::MOBILE)) { + !WriteNode(*mobile_folder_value, BookmarkNode::MOBILE) || + !WriteNode(*tabs_collection_value, BookmarkNode::TABS_COLLECTION)) { NotifyOnFinish(BookmarksExportObserver::Result::kCouldNotWriteNodes); return; } @@ -465,6 +469,8 @@ void BookmarkFaviconFetcher::ExportBookmarks() { BookmarkModelFactory::GetForBrowserContext(profile_)->other_node()); ExtractUrls( BookmarkModelFactory::GetForBrowserContext(profile_)->mobile_node()); + ExtractUrls( + BookmarkModelFactory::GetForBrowserContext(profile_)->tabs_collection_node()); if (!bookmark_urls_.empty()) FetchNextFavicon(); else diff --git a/chrome/browser/bookmarks/chrome_bookmark_client.cc b/chrome/browser/bookmarks/chrome_bookmark_client.cc --- a/chrome/browser/bookmarks/chrome_bookmark_client.cc +++ b/chrome/browser/bookmarks/chrome_bookmark_client.cc @@ -160,6 +160,8 @@ bool ChromeBookmarkClient::IsPermanentNodeVisibleWhenEmpty( return !is_mobile; case bookmarks::BookmarkNode::MOBILE: return is_mobile; + case bookmarks::BookmarkNode::TABS_COLLECTION: + return is_mobile; } return false; diff --git a/chrome/browser/download/android/java/src/org/chromium/chrome/browser/download/dialogs/DownloadLocationCustomView.java b/chrome/browser/download/android/java/src/org/chromium/chrome/browser/download/dialogs/DownloadLocationCustomView.java --- a/chrome/browser/download/android/java/src/org/chromium/chrome/browser/download/dialogs/DownloadLocationCustomView.java +++ b/chrome/browser/download/android/java/src/org/chromium/chrome/browser/download/dialogs/DownloadLocationCustomView.java @@ -79,7 +79,7 @@ public class DownloadLocationCustomView mDirectoryAdapter.update(); } - void setTitle(CharSequence title) { + public void setTitle(CharSequence title) { mTitle.setText(title); } @@ -87,7 +87,7 @@ public class DownloadLocationCustomView mSubtitleView.setText(subtitle); } - void setFileName(CharSequence fileName) { + public void setFileName(CharSequence fileName) { mFileName.setText(fileName); } diff --git a/chrome/browser/ui/android/strings/android_chrome_strings.grd b/chrome/browser/ui/android/strings/android_chrome_strings.grd --- a/chrome/browser/ui/android/strings/android_chrome_strings.grd +++ b/chrome/browser/ui/android/strings/android_chrome_strings.grd @@ -3989,6 +3989,9 @@ To change this setting, <resetlink>reset sync

Select tabs + + Bookmark all tabs + Get image descriptions diff --git a/components/bookmark_bar_strings.grdp b/components/bookmark_bar_strings.grdp --- a/components/bookmark_bar_strings.grdp +++ b/components/bookmark_bar_strings.grdp @@ -20,6 +20,9 @@ Mobile bookmarks + + Tabs collection + Other bookmarks @@ -34,6 +37,9 @@ Mobile Bookmarks + + Tabs Collection + Other Bookmarks diff --git a/components/bookmarks/browser/bookmark_codec.cc b/components/bookmarks/browser/bookmark_codec.cc --- a/components/bookmarks/browser/bookmark_codec.cc +++ b/components/bookmarks/browser/bookmark_codec.cc @@ -36,6 +36,7 @@ const char BookmarkCodec::kBookmarkBarFolderNameKey[] = "bookmark_bar"; const char BookmarkCodec::kOtherBookmarkFolderNameKey[] = "other"; // The value is left as 'synced' for historical reasons. const char BookmarkCodec::kMobileBookmarkFolderNameKey[] = "synced"; +const char BookmarkCodec::kTabsBookmarkFolderNameKey[] = "tabs"; const char BookmarkCodec::kVersionKey[] = "version"; const char BookmarkCodec::kChecksumKey[] = "checksum"; const char BookmarkCodec::kIdKey[] = "id"; @@ -75,7 +76,8 @@ BookmarkCodec::~BookmarkCodec() = default; base::Value::Dict BookmarkCodec::Encode(BookmarkModel* model, std::string sync_metadata_str) { return Encode(model->bookmark_bar_node(), model->other_node(), - model->mobile_node(), model->root_node()->GetMetaInfoMap(), + model->mobile_node(), model->tabs_collection_node(), + model->root_node()->GetMetaInfoMap(), model->root_node()->GetUnsyncedMetaInfoMap(), std::move(sync_metadata_str)); } @@ -84,6 +86,7 @@ base::Value::Dict BookmarkCodec::Encode( const BookmarkNode* bookmark_bar_node, const BookmarkNode* other_folder_node, const BookmarkNode* mobile_folder_node, + const BookmarkNode* tabs_folder_node, const BookmarkNode::MetaInfoMap* model_meta_info_map, const BookmarkNode::MetaInfoMap* model_unsynced_meta_info_map, std::string sync_metadata_str) { @@ -105,6 +108,7 @@ base::Value::Dict BookmarkCodec::Encode( roots.Set(kBookmarkBarFolderNameKey, EncodeNode(bookmark_bar_node)); roots.Set(kOtherBookmarkFolderNameKey, EncodeNode(other_folder_node)); roots.Set(kMobileBookmarkFolderNameKey, EncodeNode(mobile_folder_node)); + roots.Set(kTabsBookmarkFolderNameKey, EncodeNode(tabs_folder_node)); if (model_meta_info_map) roots.Set(kMetaInfo, EncodeMetaInfo(*model_meta_info_map)); if (model_unsynced_meta_info_map) { @@ -125,6 +129,7 @@ bool BookmarkCodec::Decode(const base::Value::Dict& value, BookmarkNode* bb_node, BookmarkNode* other_folder_node, BookmarkNode* mobile_folder_node, + BookmarkNode* tabs_folder_node, int64_t* max_id, std::string* sync_metadata_str) { ids_.clear(); @@ -132,7 +137,8 @@ bool BookmarkCodec::Decode(const base::Value::Dict& value, base::Uuid::ParseLowercase(kBookmarkBarNodeUuid), base::Uuid::ParseLowercase(kOtherBookmarksNodeUuid), base::Uuid::ParseLowercase(kMobileBookmarksNodeUuid), - base::Uuid::ParseLowercase(kManagedNodeUuid)}; + base::Uuid::ParseLowercase(kManagedNodeUuid), + base::Uuid::ParseLowercase(kTabsCollectionBookmarksNodeUuid)}; ids_reassigned_ = false; uuids_reassigned_ = false; ids_valid_ = true; @@ -140,12 +146,13 @@ bool BookmarkCodec::Decode(const base::Value::Dict& value, stored_checksum_.clear(); InitializeChecksum(); bool success = DecodeHelper(bb_node, other_folder_node, mobile_folder_node, + tabs_folder_node, value, sync_metadata_str); FinalizeChecksum(); // If either the checksums differ or some IDs were missing/not unique, // reassign IDs. if (!ids_valid_ || computed_checksum() != stored_checksum()) - ReassignIDs(bb_node, other_folder_node, mobile_folder_node); + ReassignIDs(bb_node, other_folder_node, mobile_folder_node, tabs_folder_node); *max_id = maximum_id_ + 1; return success; } @@ -201,6 +208,7 @@ base::Value::Dict BookmarkCodec::EncodeMetaInfo( bool BookmarkCodec::DecodeHelper(BookmarkNode* bb_node, BookmarkNode* other_folder_node, BookmarkNode* mobile_folder_node, + BookmarkNode* tabs_folder_node, const base::Value::Dict& value, std::string* sync_metadata_str) { absl::optional version = value.FindInt(kVersionKey); @@ -225,6 +233,8 @@ bool BookmarkCodec::DecodeHelper(BookmarkNode* bb_node, roots->FindDict(kOtherBookmarkFolderNameKey); const base::Value::Dict* mobile_folder_value = roots->FindDict(kMobileBookmarkFolderNameKey); + const base::Value::Dict* tabs_folder_value = + roots->FindDict(kTabsBookmarkFolderNameKey); if (!bb_value || !other_folder_value || !mobile_folder_value) return false; @@ -232,6 +242,8 @@ bool BookmarkCodec::DecodeHelper(BookmarkNode* bb_node, DecodeNode(*bb_value, nullptr, bb_node); DecodeNode(*other_folder_value, nullptr, other_folder_node); DecodeNode(*mobile_folder_value, nullptr, mobile_folder_node); + if (tabs_folder_value) + DecodeNode(*tabs_folder_value, nullptr, tabs_folder_node); if (!DecodeMetaInfo(*roots, &model_meta_info_map_)) return false; @@ -497,11 +509,13 @@ void BookmarkCodec::DecodeMetaInfoHelper( void BookmarkCodec::ReassignIDs(BookmarkNode* bb_node, BookmarkNode* other_node, - BookmarkNode* mobile_node) { + BookmarkNode* mobile_node, + BookmarkNode* tabs_folder_node) { maximum_id_ = 0; ReassignIDsHelper(bb_node); ReassignIDsHelper(other_node); ReassignIDsHelper(mobile_node); + ReassignIDsHelper(tabs_folder_node); ids_reassigned_ = true; } diff --git a/components/bookmarks/browser/bookmark_codec.h b/components/bookmarks/browser/bookmark_codec.h --- a/components/bookmarks/browser/bookmark_codec.h +++ b/components/bookmarks/browser/bookmark_codec.h @@ -46,6 +46,7 @@ class BookmarkCodec { const BookmarkNode* bookmark_bar_node, const BookmarkNode* other_folder_node, const BookmarkNode* mobile_folder_node, + const BookmarkNode* tabs_folder_node, const BookmarkNode::MetaInfoMap* model_meta_info_map, const BookmarkNode::MetaInfoMap* model_unsynced_meta_info_map, std::string sync_metadata_str); @@ -59,6 +60,7 @@ class BookmarkCodec { BookmarkNode* bb_node, BookmarkNode* other_folder_node, BookmarkNode* mobile_folder_node, + BookmarkNode* tabs_folder_node, int64_t* max_node_id, std::string* sync_metadata_str); @@ -110,6 +112,7 @@ class BookmarkCodec { // Allows the BookmarkClient to read and a write a string blob from the JSON // file. That string captures the bookmarks sync metadata. static const char kSyncMetadata[]; + static const char kTabsBookmarkFolderNameKey[]; static const char kDateLastUsed[]; // Possible values for kTypeKey. @@ -128,6 +131,7 @@ class BookmarkCodec { bool DecodeHelper(BookmarkNode* bb_node, BookmarkNode* other_folder_node, BookmarkNode* mobile_folder_node, + BookmarkNode* tabs_folder_node, const base::Value::Dict& value, std::string* sync_metadata_str); @@ -139,7 +143,8 @@ class BookmarkCodec { // Reassigns bookmark IDs for all nodes. void ReassignIDs(BookmarkNode* bb_node, BookmarkNode* other_node, - BookmarkNode* mobile_node); + BookmarkNode* mobile_node, + BookmarkNode* tabs_folder_node); // Helper to recursively reassign IDs. void ReassignIDsHelper(BookmarkNode* node); diff --git a/components/bookmarks/browser/bookmark_load_details.cc b/components/bookmarks/browser/bookmark_load_details.cc --- a/components/bookmarks/browser/bookmark_load_details.cc +++ b/components/bookmarks/browser/bookmark_load_details.cc @@ -37,6 +37,10 @@ BookmarkLoadDetails::BookmarkLoadDetails(BookmarkClient* client) root_node_->Add(BookmarkPermanentNode::CreateMobileBookmarks( max_id_++, client->IsPermanentNodeVisibleWhenEmpty(BookmarkNode::MOBILE)))); + tabs_collection_folder_node_ = static_cast( + root_node_->Add(BookmarkPermanentNode::CreateTabsCollectionBookmarks( + max_id_++, + client->IsPermanentNodeVisibleWhenEmpty(BookmarkNode::TABS_COLLECTION)))); } BookmarkLoadDetails::~BookmarkLoadDetails() = default; diff --git a/components/bookmarks/browser/bookmark_load_details.h b/components/bookmarks/browser/bookmark_load_details.h --- a/components/bookmarks/browser/bookmark_load_details.h +++ b/components/bookmarks/browser/bookmark_load_details.h @@ -47,6 +47,7 @@ class BookmarkLoadDetails { BookmarkPermanentNode* bb_node() { return bb_node_; } BookmarkPermanentNode* mobile_folder_node() { return mobile_folder_node_; } BookmarkPermanentNode* other_folder_node() { return other_folder_node_; } + BookmarkPermanentNode* tabs_collection_folder_node() { return tabs_collection_folder_node_; } std::unique_ptr owned_titled_url_index() { return std::move(titled_url_index_); @@ -119,6 +120,8 @@ class BookmarkLoadDetails { nullptr; raw_ptr mobile_folder_node_ = nullptr; + raw_ptr tabs_collection_folder_node_ = + nullptr; LoadManagedNodeCallback load_managed_node_callback_; std::unique_ptr titled_url_index_; UuidIndex uuid_index_; diff --git a/components/bookmarks/browser/bookmark_model.cc b/components/bookmarks/browser/bookmark_model.cc --- a/components/bookmarks/browser/bookmark_model.cc +++ b/components/bookmarks/browser/bookmark_model.cc @@ -845,7 +845,7 @@ bool BookmarkModel::HasBookmarks() const { bool BookmarkModel::HasNoUserCreatedBookmarksOrFolders() const { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); return bookmark_bar_node_->children().empty() && - other_node_->children().empty() && mobile_node_->children().empty(); + other_node_->children().empty() && mobile_node_->children().empty() && tabs_collection_node_->children().empty(); } bool BookmarkModel::IsBookmarked(const GURL& url) const { @@ -1155,6 +1155,7 @@ void BookmarkModel::DoneLoading(std::unique_ptr details) { bookmark_bar_node_ = details->bb_node(); other_node_ = details->other_folder_node(); mobile_node_ = details->mobile_folder_node(); + tabs_collection_node_ = details->tabs_collection_folder_node(); titled_url_index_->SetNodeSorter( std::make_unique(client_.get())); diff --git a/components/bookmarks/browser/bookmark_model.h b/components/bookmarks/browser/bookmark_model.h --- a/components/bookmarks/browser/bookmark_model.h +++ b/components/bookmarks/browser/bookmark_model.h @@ -132,6 +132,12 @@ class BookmarkModel final : public BookmarkUndoProvider, return mobile_node_; } + // Returns the 'mobile' node. This is NULL until loaded. + const BookmarkNode* tabs_collection_node() const { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + return tabs_collection_node_; + } + bool is_root_node(const BookmarkNode* node) const { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); return node == root_; @@ -495,6 +501,7 @@ class BookmarkModel final : public BookmarkUndoProvider, nullptr; raw_ptr mobile_node_ = nullptr; + raw_ptr tabs_collection_node_ = nullptr; // The maximum ID assigned to the bookmark nodes in the model. int64_t next_node_id_ = 1; diff --git a/components/bookmarks/browser/bookmark_node.cc b/components/bookmarks/browser/bookmark_node.cc --- a/components/bookmarks/browser/bookmark_node.cc +++ b/components/bookmarks/browser/bookmark_node.cc @@ -239,6 +239,17 @@ BookmarkPermanentNode::CreateMobileBookmarks(int64_t id, visible_when_empty)); } +// static +std::unique_ptr +BookmarkPermanentNode::CreateTabsCollectionBookmarks(int64_t id, + bool visible_when_empty) { + // base::WrapUnique() used because the constructor is private. + return base::WrapUnique(new BookmarkPermanentNode( + id, TABS_COLLECTION, base::Uuid::ParseLowercase(kTabsCollectionBookmarksNodeUuid), + l10n_util::GetStringUTF16(IDS_BOOKMARK_BAR_TABS_COLLECTION_FOLDER_NAME), + visible_when_empty)); +} + BookmarkPermanentNode::BookmarkPermanentNode(int64_t id, Type type, const base::Uuid& uuid, diff --git a/components/bookmarks/browser/bookmark_node.h b/components/bookmarks/browser/bookmark_node.h --- a/components/bookmarks/browser/bookmark_node.h +++ b/components/bookmarks/browser/bookmark_node.h @@ -35,6 +35,7 @@ class BookmarkNode : public ui::TreeNode, public TitledUrlNode { FOLDER, BOOKMARK_BAR, OTHER_NODE, + TABS_COLLECTION, MOBILE }; @@ -259,6 +260,9 @@ class BookmarkPermanentNode : public BookmarkNode { static std::unique_ptr CreateMobileBookmarks( int64_t id, bool visible_when_empty); + static std::unique_ptr CreateTabsCollectionBookmarks( + int64_t id, + bool visible_when_empty); // Constructor is private to disallow the construction of permanent nodes // other than the well-known ones, see factory methods. diff --git a/components/bookmarks/browser/bookmark_uuids.cc b/components/bookmarks/browser/bookmark_uuids.cc --- a/components/bookmarks/browser/bookmark_uuids.cc +++ b/components/bookmarks/browser/bookmark_uuids.cc @@ -35,6 +35,9 @@ const char kManagedNodeUuid[] = "323123f4-9381-5aee-80e6-ea5fca2f7672"; // see https://crbug.com/1484372 for details. const char kShoppingCollectionUuid[] = "89fc5b66-beb6-56c1-a99b-70635d7df201"; +const char kTabsCollectionBookmarksNodeUuid[] = + "00000000-0000-4000-a000-000000000006"; + // This value is the result of exercising sync's function // syncer::InferGuidForLegacyBookmark() with an empty input. const char kBannedUuidDueToPastSyncBug[] = diff --git a/components/bookmarks/browser/bookmark_uuids.h b/components/bookmarks/browser/bookmark_uuids.h --- a/components/bookmarks/browser/bookmark_uuids.h +++ b/components/bookmarks/browser/bookmark_uuids.h @@ -14,6 +14,7 @@ extern const char kBookmarkBarNodeUuid[]; extern const char kOtherBookmarksNodeUuid[]; extern const char kMobileBookmarksNodeUuid[]; extern const char kManagedNodeUuid[]; +extern const char kTabsCollectionBookmarksNodeUuid[]; extern const char kShoppingCollectionUuid[]; // A bug in sync caused some problematic UUIDs to be produced. diff --git a/components/bookmarks/browser/model_loader.cc b/components/bookmarks/browser/model_loader.cc --- a/components/bookmarks/browser/model_loader.cc +++ b/components/bookmarks/browser/model_loader.cc @@ -45,7 +45,8 @@ void LoadBookmarks(const base::FilePath& path, std::string sync_metadata_str; BookmarkCodec codec; codec.Decode(*root_dict, details->bb_node(), details->other_folder_node(), - details->mobile_folder_node(), &max_node_id, + details->mobile_folder_node(), + details->tabs_collection_folder_node(), &max_node_id, &sync_metadata_str); details->set_sync_metadata_str(std::move(sync_metadata_str)); details->set_max_id(std::max(max_node_id, details->max_id())); diff --git a/components/sync_bookmarks/bookmark_specifics_conversions.cc b/components/sync_bookmarks/bookmark_specifics_conversions.cc --- a/components/sync_bookmarks/bookmark_specifics_conversions.cc +++ b/components/sync_bookmarks/bookmark_specifics_conversions.cc @@ -447,6 +447,7 @@ sync_pb::BookmarkSpecifics::Type GetProtoTypeFromBookmarkNode( case bookmarks::BookmarkNode::BOOKMARK_BAR: case bookmarks::BookmarkNode::OTHER_NODE: case bookmarks::BookmarkNode::MOBILE: + case bookmarks::BookmarkNode::TABS_COLLECTION: DCHECK(node->is_folder()); return sync_pb::BookmarkSpecifics::FOLDER; } -- 2.25.1