907 lines
45 KiB
Diff
907 lines
45 KiB
Diff
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/values/ids.xml | 1 +
|
|
.../chrome/browser/ChromeTabbedActivity.java | 43 +++++++++++++++++++
|
|
.../TabbedAppMenuPropertiesDelegate.java | 15 +++++++
|
|
.../bookmarks/android/bookmark_bridge.cc | 42 ++++++++++++++++++
|
|
.../bookmarks/android/bookmark_bridge.h | 8 ++++
|
|
.../browser/bookmarks/BookmarkBridge.java | 41 ++++++++++++++++++
|
|
.../browser/bookmarks/bookmark_html_writer.cc | 14 +++++-
|
|
.../bookmarks/bookmark_parent_folder.cc | 1 +
|
|
.../permanent_folder_ordering_tracker.cc | 2 +
|
|
.../dialogs/DownloadLocationCustomView.java | 4 +-
|
|
.../strings/android_chrome_strings.grd | 6 +--
|
|
.../bookmark_ui_operations_helper.cc | 2 +
|
|
.../bookmark_bar_page_handler.cc | 2 +
|
|
components/bookmark_bar_strings.grdp | 6 +++
|
|
.../bookmarks/browser/bookmark_codec.cc | 14 +++++-
|
|
components/bookmarks/browser/bookmark_codec.h | 4 ++
|
|
.../browser/bookmark_load_details.cc | 15 +++++--
|
|
.../bookmarks/browser/bookmark_load_details.h | 2 +
|
|
.../bookmarks/browser/bookmark_model.cc | 3 +-
|
|
components/bookmarks/browser/bookmark_model.h | 8 ++++
|
|
components/bookmarks/browser/bookmark_node.cc | 13 ++++++
|
|
components/bookmarks/browser/bookmark_node.h | 4 ++
|
|
.../bookmarks/browser/bookmark_storage.cc | 2 +
|
|
.../bookmarks/browser/bookmark_uuids.cc | 3 ++
|
|
components/bookmarks/browser/bookmark_uuids.h | 1 +
|
|
components/bookmarks/browser/model_loader.cc | 9 +++-
|
|
.../bookmark_specifics_conversions.cc | 1 +
|
|
27 files changed, 252 insertions(+), 14 deletions(-)
|
|
|
|
diff --git a/chrome/android/java/res/values/ids.xml b/chrome/android/java/res/values/ids.xml
|
|
--- a/chrome/android/java/res/values/ids.xml
|
|
+++ b/chrome/android/java/res/values/ids.xml
|
|
@@ -206,6 +206,7 @@ found in the LICENSE file.
|
|
<item type="id" name="duplicate_tab_menu_id" />
|
|
<item type="id" name="glic_menu_id" />
|
|
<item type="id" name="open_in_app_menu_id" />
|
|
+ <item type="id" name="bookmark_all_tabs_menu_id" />
|
|
|
|
<!-- Custom Tabs -->
|
|
<item type="id" name="view_id_tag_key" />
|
|
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
|
|
@@ -114,6 +114,7 @@ import org.chromium.chrome.browser.bookmarks.BookmarkPane;
|
|
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;
|
|
@@ -333,6 +334,9 @@ import org.chromium.chrome.browser.ui.browser_window.BrowserWindowType;
|
|
import org.chromium.chrome.browser.ui.default_browser_promo.DefaultBrowserPromoUtils;
|
|
import org.chromium.chrome.browser.ui.edge_to_edge.EdgeToEdgeUtils;
|
|
import org.chromium.chrome.browser.ui.edge_to_edge.TransitiveTopInsetProvider;
|
|
+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.browser.ui.searchactivityutils.SearchActivityClient;
|
|
import org.chromium.chrome.browser.ui.searchactivityutils.SearchActivityExtras.IntentOrigin;
|
|
import org.chromium.chrome.browser.ui.signin.BottomSheetSigninAndHistorySyncConfig;
|
|
@@ -350,6 +354,8 @@ import org.chromium.chrome.browser.util.ChromeAccessibilityUtil;
|
|
import org.chromium.chrome.browser.util.DefaultBrowserInfo;
|
|
import org.chromium.chrome.browser.xr.scenecore.XrModule;
|
|
import org.chromium.components.browser_ui.accessibility.AccessibilityFeatureMap;
|
|
+import org.chromium.components.bookmarks.BookmarkId;
|
|
+import org.chromium.components.bookmarks.BookmarkItem;
|
|
import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
|
|
import org.chromium.components.browser_ui.share.ShareParams;
|
|
import org.chromium.components.browser_ui.util.BrowserControlsVisibilityDelegate;
|
|
@@ -4188,6 +4194,8 @@ public class ChromeTabbedActivity extends ChromeActivity implements PreAttachInt
|
|
CloseAllTabsDialog.show(
|
|
this, getModalDialogManagerSupplier(), tabModelSelector, closeAllTabsRunnable);
|
|
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) {
|
|
boolean allowUndo = TabClosureParamsUtils.shouldAllowUndo(triggeringMotion);
|
|
|
|
@@ -4335,6 +4343,41 @@ public class ChromeTabbedActivity extends ChromeActivity implements PreAttachInt
|
|
getTabModalLifetimeHandler().onOmniboxFocusChanged(hasFocus);
|
|
}
|
|
|
|
+ private void bookmarkAllTabs() {
|
|
+ TabModel tabModel = getTabModelSelector().getCurrentModel();
|
|
+ int count = tabModel.getCount();
|
|
+ Log.i(TAG, "bookmarkAllTabs(): %d tabs to bookmark", count);
|
|
+ if (count == 0) {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ final BookmarkModel bookmarkModel = mBookmarkModelSupplier.get();
|
|
+ bookmarkModel.finishLoadingBookmarkModel(() -> {
|
|
+ for (int i = 0; i < tabModel.getCount(); i++) {
|
|
+ Tab tab = tabModel.getTabAt(i);
|
|
+ if (tab.isNativePage()) {
|
|
+ continue;
|
|
+ }
|
|
+ bookmarkModel.addToTabsCollection(tab);
|
|
+ }
|
|
+ BookmarkId parent = bookmarkModel.getTabsCollectionFolderId();
|
|
+ BookmarkItem bookmarkItem = bookmarkModel.getBookmarkById(parent);
|
|
+ String folderName = "";
|
|
+ if (bookmarkItem != null) {
|
|
+ folderName = bookmarkItem.getTitle();
|
|
+ }
|
|
+ SnackbarController snackbarController = new SnackbarController() {
|
|
+ @Override
|
|
+ public void onAction(Object actionData) {
|
|
+ }
|
|
+ };
|
|
+ Snackbar snackbar = Snackbar.make(folderName, snackbarController, Snackbar.TYPE_ACTION,
|
|
+ Snackbar.UMA_BOOKMARK_ADDED)
|
|
+ .setTemplateText(getString(R.string.bookmark_page_saved_folder));
|
|
+ getSnackbarManager().showSnackbar(snackbar);
|
|
+ });
|
|
+ }
|
|
+
|
|
private void recordLauncherShortcutAction(boolean isIncognito) {
|
|
if (isIncognito) {
|
|
RecordUserAction.record("Android.LauncherShortcut.NewIncognitoTab");
|
|
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/TabbedAppMenuPropertiesDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/TabbedAppMenuPropertiesDelegate.java
|
|
--- a/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/TabbedAppMenuPropertiesDelegate.java
|
|
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/TabbedAppMenuPropertiesDelegate.java
|
|
@@ -328,6 +328,14 @@ public class TabbedAppMenuPropertiesDelegate extends AppMenuPropertiesDelegateIm
|
|
// Bookmarks
|
|
modelList.add(buildBookmarksItem());
|
|
|
|
+ // Add bookmark all item
|
|
+ modelList.add(new MVCListAdapter.ListItem(
|
|
+ AppMenuHandler.AppMenuItemType.STANDARD,
|
|
+ buildModelForStandardMenuItem(
|
|
+ R.id.bookmark_all_tabs_menu_id,
|
|
+ R.string.menu_bookmark_all_tabs,
|
|
+ shouldShowIconBeforeItem() ? R.drawable.ic_folder_blue_24dp : 0)));
|
|
+
|
|
// Recent Tabs
|
|
if (!ChromeFeatureList.isEnabled(ChromeFeatureList.SUBMENUS_IN_APP_MENU)
|
|
&& shouldShowRecentTabsItem()) {
|
|
@@ -551,6 +559,13 @@ public class TabbedAppMenuPropertiesDelegate extends AppMenuPropertiesDelegateIm
|
|
modelList.add(buildNewTabGroupItem());
|
|
modelList.add(buildCloseAllTabsItem());
|
|
if (shouldShowSelectTabsItem()) modelList.add(buildSelectTabsItem());
|
|
+ // Add bookmark all item
|
|
+ modelList.add(new MVCListAdapter.ListItem(
|
|
+ AppMenuHandler.AppMenuItemType.STANDARD,
|
|
+ buildModelForStandardMenuItem(
|
|
+ R.id.bookmark_all_tabs_menu_id,
|
|
+ R.string.menu_bookmark_all_tabs,
|
|
+ shouldShowIconBeforeItem() ? R.drawable.ic_folder_blue_24dp : 0)));
|
|
if (shouldShowQuickDeleteItem()) modelList.add(buildQuickDeleteItem());
|
|
modelList.add(buildSettingsItem());
|
|
}
|
|
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
|
|
@@ -337,6 +337,33 @@ bool BookmarkBridge::AreAccountBookmarkFoldersActive(JNIEnv* env) {
|
|
return bookmark_model_->account_mobile_node() != nullptr;
|
|
}
|
|
|
|
+base::android::ScopedJavaLocalRef<jobject>
|
|
+BookmarkBridge::GetBookmarkIdForTabsCollection(
|
|
+ JNIEnv* env,
|
|
+ const JavaRef<jobject>& url) {
|
|
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
|
+
|
|
+ GURL gurl = url::GURLAndroid::ToNativeGURL(env, url);
|
|
+
|
|
+ bookmarks::BookmarkModel* model =
|
|
+ BookmarkModelFactory::GetForBrowserContext(profile_);
|
|
+
|
|
+ std::vector<raw_ptr<const bookmarks::BookmarkNode, VectorExperimental>> nodes =
|
|
+ model->GetNodesByURL(gurl);
|
|
+ std::sort(nodes.begin(), nodes.end(), &bookmarks::MoreRecentlyAdded);
|
|
+
|
|
+ for (const BookmarkNode* 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<jobject>
|
|
BookmarkBridge::GetMostRecentlyAddedUserBookmarkIdForUrl(JNIEnv* env,
|
|
const GURL& url) {
|
|
@@ -448,6 +475,7 @@ void BookmarkBridge::GetAllFoldersWithDepths(
|
|
// Vector to temporarily contain all child bookmarks at same level for sorting
|
|
std::vector<const BookmarkNode*> bookmarks = {
|
|
bookmark_model_->mobile_node(),
|
|
+ bookmark_model_->tabs_collection_node(),
|
|
bookmark_model_->bookmark_bar_node(),
|
|
bookmark_model_->other_node(),
|
|
};
|
|
@@ -556,6 +584,9 @@ std::vector<const BookmarkNode*> BookmarkBridge::GetTopLevelFolderIdsImpl(
|
|
top_level_folders.push_back(other_node);
|
|
}
|
|
|
|
+ const BookmarkNode* tabs_collection_node = bookmark_model_->tabs_collection_node();
|
|
+ top_level_folders.push_back(tabs_collection_node);
|
|
+
|
|
const BookmarkNode* reading_list_node =
|
|
local_or_syncable_reading_list_manager_->GetRoot();
|
|
if (IsPermanentFolderVisible(
|
|
@@ -1223,6 +1254,17 @@ void BookmarkBridge::GetBookmarksOfType(
|
|
AddBookmarkNodesToBookmarkIdList(env, j_list, results);
|
|
}
|
|
|
|
+ScopedJavaLocalRef<jobject> BookmarkBridge::GetTabsCollectionFolderId(
|
|
+ JNIEnv* env,
|
|
+ const JavaRef<jobject>& obj) {
|
|
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
|
+ const BookmarkNode* tabs_collection_node = bookmark_model_->tabs_collection_node();
|
|
+ ScopedJavaLocalRef<jobject> folder_id_obj =
|
|
+ JavaBookmarkIdCreateBookmarkId(
|
|
+ env, tabs_collection_node->id(), GetBookmarkType(tabs_collection_node));
|
|
+ return folder_id_obj;
|
|
+}
|
|
+
|
|
ScopedJavaLocalRef<jobject> BookmarkBridge::AddFolder(
|
|
JNIEnv* env,
|
|
const JavaRef<jobject>& 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
|
|
@@ -105,6 +105,10 @@ class BookmarkBridge : public ProfileObserver,
|
|
|
|
bool AreAccountBookmarkFoldersActive(JNIEnv* env);
|
|
|
|
+ base::android::ScopedJavaLocalRef<jobject> GetBookmarkIdForTabsCollection(
|
|
+ JNIEnv* env,
|
|
+ const base::android::JavaRef<jobject>& url);
|
|
+
|
|
base::android::ScopedJavaLocalRef<jobject>
|
|
GetMostRecentlyAddedUserBookmarkIdForUrl(JNIEnv* env, const GURL& url);
|
|
const bookmarks::BookmarkNode* GetMostRecentlyAddedUserBookmarkIdForUrlImpl(
|
|
@@ -232,6 +236,10 @@ class BookmarkBridge : public ProfileObserver,
|
|
const base::android::JavaRef<jobject>& j_list,
|
|
int32_t type);
|
|
|
|
+ base::android::ScopedJavaLocalRef<jobject> GetTabsCollectionFolderId(
|
|
+ JNIEnv* env,
|
|
+ const base::android::JavaRef<jobject>& obj);
|
|
+
|
|
base::android::ScopedJavaLocalRef<jobject> AddFolder(
|
|
JNIEnv* env,
|
|
const base::android::JavaRef<jobject>& j_parent_id_obj,
|
|
diff --git a/chrome/browser/bookmarks/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkBridge.java b/chrome/browser/bookmarks/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkBridge.java
|
|
--- a/chrome/browser/bookmarks/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkBridge.java
|
|
+++ b/chrome/browser/bookmarks/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkBridge.java
|
|
@@ -16,6 +16,7 @@ import org.jni_zero.JniType;
|
|
import org.jni_zero.NativeMethods;
|
|
|
|
import org.chromium.base.ContextUtils;
|
|
+import org.chromium.base.Log;
|
|
import org.chromium.base.ObserverList;
|
|
import org.chromium.base.ThreadUtils;
|
|
import org.chromium.base.metrics.RecordUserAction;
|
|
@@ -26,6 +27,7 @@ import org.chromium.chrome.browser.partnerbookmarks.PartnerBookmarksShim;
|
|
import org.chromium.chrome.browser.preferences.ChromePreferenceKeys;
|
|
import org.chromium.chrome.browser.preferences.ChromeSharedPreferences;
|
|
import org.chromium.chrome.browser.profiles.Profile;
|
|
+import org.chromium.chrome.browser.tab.Tab;
|
|
import org.chromium.components.bookmarks.BookmarkId;
|
|
import org.chromium.components.bookmarks.BookmarkItem;
|
|
import org.chromium.components.bookmarks.BookmarkType;
|
|
@@ -47,6 +49,7 @@ import java.util.function.BiConsumer;
|
|
class BookmarkBridge {
|
|
private static final OneshotSupplierImpl<BookmarkModel.PartnerBookmarkIteratorProvider>
|
|
sPartnerBookmarkIteratorSupplier = new OneshotSupplierImpl<>();
|
|
+ private static final String TAG = "BookmarkBridge";
|
|
|
|
private final ObserverList<BookmarkModelObserver> mObservers = new ObserverList<>();
|
|
private final Profile mProfile;
|
|
@@ -385,6 +388,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);
|
|
+ }
|
|
+
|
|
/** Returns Id representing the special "other" folder from bookmark model. */
|
|
public @Nullable BookmarkId getOtherFolderId() {
|
|
ThreadUtils.assertOnUiThread();
|
|
@@ -882,6 +895,29 @@ class BookmarkBridge {
|
|
.addToReadingList(mNativeBookmarkBridge, parentId, title, url);
|
|
}
|
|
|
|
+ // Used to bookmark all tabs in a specific folder, created if not existing
|
|
+ public BookmarkId addToTabsCollection(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;
|
|
+ }
|
|
+
|
|
/**
|
|
* Helper method to mark an item as read.
|
|
*
|
|
@@ -1099,6 +1135,9 @@ class BookmarkBridge {
|
|
BookmarkId getMostRecentlyAddedUserBookmarkIdForUrl(
|
|
long nativeBookmarkBridge, @JniType("GURL") GURL url);
|
|
|
|
+ BookmarkId getBookmarkIdForTabsCollection(
|
|
+ long nativeBookmarkBridge, GURL url);
|
|
+
|
|
BookmarkItem getBookmarkById(long nativeBookmarkBridge, long id, int type);
|
|
|
|
void getTopLevelFolderIds(
|
|
@@ -1120,6 +1159,8 @@ class BookmarkBridge {
|
|
|
|
BookmarkId getRootFolderId(long nativeBookmarkBridge);
|
|
|
|
+ BookmarkId getTabsCollectionFolderId(long nativeBookmarkBridge, BookmarkBridge caller);
|
|
+
|
|
BookmarkId getMobileFolderId(long nativeBookmarkBridge);
|
|
|
|
BookmarkId getOtherFolderId(long nativeBookmarkBridge);
|
|
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
|
|
@@ -169,14 +169,15 @@ class Writer : public base::RefCountedThreadSafe<Writer> {
|
|
BookmarkCodec codec;
|
|
local_bookmarks_ =
|
|
codec.Encode(model->bookmark_bar_node(), model->other_node(),
|
|
- model->mobile_node(), /*sync_metadata_str=*/std::string());
|
|
+ model->mobile_node(), model->tabs_collection_node(),
|
|
+ /*sync_metadata_str=*/std::string());
|
|
|
|
if (model->account_bookmark_bar_node()) {
|
|
CHECK(model->account_other_node());
|
|
CHECK(model->account_mobile_node());
|
|
account_bookmarks_ = codec.Encode(
|
|
model->account_bookmark_bar_node(), model->account_other_node(),
|
|
- model->account_mobile_node(), /*sync_metadata_str=*/std::string());
|
|
+ model->account_mobile_node(), /*tabs_folder_node*/ nullptr, /*sync_metadata_str=*/std::string());
|
|
} else {
|
|
CHECK(!model->account_other_node());
|
|
CHECK(!model->account_mobile_node());
|
|
@@ -210,6 +211,9 @@ class Writer : public base::RefCountedThreadSafe<Writer> {
|
|
base::DictValue* mobile_folder_value = local_permanent_folders->FindDict(
|
|
BookmarkCodec::kMobileBookmarkFolderNameKey);
|
|
CHECK(mobile_folder_value);
|
|
+ base::DictValue* tabs_collection_value =
|
|
+ local_permanent_folders->FindDict(BookmarkCodec::kTabsBookmarkFolderNameKey);
|
|
+ CHECK(tabs_collection_value);
|
|
|
|
base::DictValue* account_permanent_folders =
|
|
account_bookmarks_.FindDict(BookmarkCodec::kRootsKey);
|
|
@@ -285,6 +289,10 @@ class Writer : public base::RefCountedThreadSafe<Writer> {
|
|
return bookmark_html_writer::Result::kCouldNotWriteNodes;
|
|
}
|
|
|
|
+ if (!WriteDescendants(*tabs_collection_value)) {
|
|
+ return bookmark_html_writer::Result::kCouldNotWriteNodes;
|
|
+ }
|
|
+
|
|
DecrementIndent();
|
|
|
|
if (!Write(kFolderChildrenEnd) || !Write(kNewline)) {
|
|
@@ -431,6 +439,7 @@ class Writer : public base::RefCountedThreadSafe<Writer> {
|
|
case BookmarkNode::URL:
|
|
case BookmarkNode::OTHER_NODE:
|
|
case BookmarkNode::MOBILE:
|
|
+ case BookmarkNode::TABS_COLLECTION:
|
|
NOTREACHED();
|
|
}
|
|
|
|
@@ -559,6 +568,7 @@ void BookmarkFaviconFetcher::ExportBookmarks() {
|
|
ExtractUrls(model->bookmark_bar_node());
|
|
ExtractUrls(model->other_node());
|
|
ExtractUrls(model->mobile_node());
|
|
+ ExtractUrls(model->tabs_collection_node());
|
|
|
|
if (model->account_bookmark_bar_node()) {
|
|
CHECK(model->account_other_node());
|
|
diff --git a/chrome/browser/bookmarks/bookmark_parent_folder.cc b/chrome/browser/bookmarks/bookmark_parent_folder.cc
|
|
--- a/chrome/browser/bookmarks/bookmark_parent_folder.cc
|
|
+++ b/chrome/browser/bookmarks/bookmark_parent_folder.cc
|
|
@@ -18,6 +18,7 @@ BookmarkParentFolder GetBookmarkParentFolderFromPermanentNode(
|
|
CHECK(node->is_permanent_node());
|
|
switch (node->type()) {
|
|
case bookmarks::BookmarkNode::URL:
|
|
+ case bookmarks::BookmarkNode::TABS_COLLECTION:
|
|
NOTREACHED();
|
|
case bookmarks::BookmarkNode::FOLDER:
|
|
// TODO(crbug.com/381252292): Consider extending type with a value
|
|
diff --git a/chrome/browser/bookmarks/permanent_folder_ordering_tracker.cc b/chrome/browser/bookmarks/permanent_folder_ordering_tracker.cc
|
|
--- a/chrome/browser/bookmarks/permanent_folder_ordering_tracker.cc
|
|
+++ b/chrome/browser/bookmarks/permanent_folder_ordering_tracker.cc
|
|
@@ -26,6 +26,7 @@ bool IsValidTrackedType(BookmarkNode::Type type) {
|
|
switch (type) {
|
|
case bookmarks::BookmarkNode::URL:
|
|
case bookmarks::BookmarkNode::FOLDER:
|
|
+ case bookmarks::BookmarkNode::TABS_COLLECTION:
|
|
NOTREACHED();
|
|
|
|
case bookmarks::BookmarkNode::BOOKMARK_BAR:
|
|
@@ -303,6 +304,7 @@ void PermanentFolderOrderingTracker::SetTrackedPermanentNodes() {
|
|
switch (tracked_type_) {
|
|
case bookmarks::BookmarkNode::URL:
|
|
case bookmarks::BookmarkNode::FOLDER:
|
|
+ case bookmarks::BookmarkNode::TABS_COLLECTION:
|
|
NOTREACHED();
|
|
|
|
case bookmarks::BookmarkNode::BOOKMARK_BAR:
|
|
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
|
|
@@ -88,7 +88,7 @@ public class DownloadLocationCustomView extends ScrollView
|
|
mDirectoryAdapter.update();
|
|
}
|
|
|
|
- void setTitle(CharSequence title) {
|
|
+ public void setTitle(CharSequence title) {
|
|
mTitle.setText(title);
|
|
}
|
|
|
|
@@ -96,7 +96,7 @@ public class DownloadLocationCustomView extends ScrollView
|
|
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
|
|
@@ -4703,6 +4703,9 @@ To change this setting, <ph name="BEGIN_LINK">BEGIN_LINK</ph>delete the Chrome d
|
|
<message name="IDS_MENU_SELECT_TABS" desc="Menu item for bulk editing tabs. [CHAR_LIMIT=27]">
|
|
Select tabs
|
|
</message>
|
|
+ <message name="IDS_MENU_BOOKMARK_ALL_TABS" desc="Menu item to bookmark all tabs. [CHAR-LIMIT=27]">
|
|
+ Bookmark all tabs
|
|
+ </message>
|
|
<message name="IDS_MENU_GET_IMAGE_DESCRIPTIONS" desc="Menu item to start getting alt text accessibility image labels/descriptions [CHAR_LIMIT=27]">
|
|
Get image descriptions
|
|
</message>
|
|
@@ -4742,9 +4745,6 @@ To change this setting, <ph name="BEGIN_LINK">BEGIN_LINK</ph>delete the Chrome d
|
|
<message name="IDS_MENU_REOPEN_CLOSED_GROUP" desc="Menu item for reopening the last closed tab group.">
|
|
Reopen closed group
|
|
</message>
|
|
- <message name="IDS_MENU_BOOKMARK_ALL_TABS" desc="Menu item for bookmarking all tabs.">
|
|
- Bookmark all tabs
|
|
- </message>
|
|
|
|
<!-- Bookmarks strings -->
|
|
<message name="IDS_BOOKMARKS" desc="Title of the bookmarks page, which shows a list of the user's bookmarks. [CHAR_LIMIT=18]">
|
|
diff --git a/chrome/browser/ui/bookmarks/bookmark_ui_operations_helper.cc b/chrome/browser/ui/bookmarks/bookmark_ui_operations_helper.cc
|
|
--- a/chrome/browser/ui/bookmarks/bookmark_ui_operations_helper.cc
|
|
+++ b/chrome/browser/ui/bookmarks/bookmark_ui_operations_helper.cc
|
|
@@ -114,6 +114,8 @@ ui::mojom::DragOperation BookmarkUIOperationsHelper::DropBookmarks(
|
|
if (is_reorder) {
|
|
base::UmaHistogramEnumeration("Bookmarks.ReorderDropTarget", target);
|
|
switch (target_parent()->GetType()) {
|
|
+ case bookmarks::BookmarkNode::TABS_COLLECTION:
|
|
+ break;
|
|
case bookmarks::BookmarkNode::URL:
|
|
NOTREACHED();
|
|
case bookmarks::BookmarkNode::FOLDER:
|
|
diff --git a/chrome/browser/ui/webui_browser/bookmark_bar_page_handler.cc b/chrome/browser/ui/webui_browser/bookmark_bar_page_handler.cc
|
|
--- a/chrome/browser/ui/webui_browser/bookmark_bar_page_handler.cc
|
|
+++ b/chrome/browser/ui/webui_browser/bookmark_bar_page_handler.cc
|
|
@@ -37,6 +37,8 @@ bookmark_bar::mojom::BookmarkType ConvertType(
|
|
return bookmark_bar::mojom::BookmarkType::OTHER_NODE;
|
|
case bookmarks::BookmarkNode::MOBILE:
|
|
return bookmark_bar::mojom::BookmarkType::MOBILE;
|
|
+ case bookmarks::BookmarkNode::TABS_COLLECTION:
|
|
+ NOTREACHED();
|
|
}
|
|
NOTREACHED();
|
|
}
|
|
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
|
|
@@ -15,6 +15,9 @@
|
|
<message name="IDS_BOOKMARK_BAR_MOBILE_FOLDER_NAME" desc="Name shown in the tree for the mobile bookmarks folder">
|
|
Mobile bookmarks
|
|
</message>
|
|
+ <message name="IDS_BOOKMARK_BAR_TABS_COLLECTION_FOLDER_NAME" desc="Name shown in the tree for the tabs collection bookmarks folder">
|
|
+ Tabs collection
|
|
+ </message>
|
|
<message name="IDS_BOOKMARK_BAR_OTHER_FOLDER_NAME" desc="Name shown in the tree for the other bookmarks folder">
|
|
Other bookmarks
|
|
</message>
|
|
@@ -29,6 +32,9 @@
|
|
<message name="IDS_BOOKMARK_BAR_MOBILE_FOLDER_NAME" desc="In Title Case: Name shown in the tree for the mobile bookmarks folder">
|
|
Mobile Bookmarks
|
|
</message>
|
|
+ <message name="IDS_BOOKMARK_BAR_TABS_COLLECTION_FOLDER_NAME" desc="Name shown in the tree for the tabs collection bookmarks folder">
|
|
+ Tabs Collection
|
|
+ </message>
|
|
<message name="IDS_BOOKMARK_BAR_OTHER_FOLDER_NAME" desc="In Title Case: Name shown in the tree for the other bookmarks folder">
|
|
Other Bookmarks
|
|
</message>
|
|
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
|
|
@@ -38,6 +38,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::kChecksumSHA256Key[] = "checksum_sha256";
|
|
@@ -97,6 +98,7 @@ BookmarkCodec::~BookmarkCodec() = default;
|
|
base::DictValue BookmarkCodec::Encode(const BookmarkNode* bookmark_bar_node,
|
|
const BookmarkNode* other_folder_node,
|
|
const BookmarkNode* mobile_folder_node,
|
|
+ const BookmarkNode* tabs_folder_node,
|
|
std::string sync_metadata_str) {
|
|
ids_reassigned_ = false;
|
|
uuids_reassigned_ = false;
|
|
@@ -122,6 +124,8 @@ base::DictValue BookmarkCodec::Encode(const BookmarkNode* bookmark_bar_node,
|
|
roots.Set(kBookmarkBarFolderNameKey, EncodeNode(bookmark_bar_node));
|
|
roots.Set(kOtherBookmarkFolderNameKey, EncodeNode(other_folder_node));
|
|
roots.Set(kMobileBookmarkFolderNameKey, EncodeNode(mobile_folder_node));
|
|
+ if (tabs_folder_node)
|
|
+ roots.Set(kTabsBookmarkFolderNameKey, EncodeNode(tabs_folder_node));
|
|
} else {
|
|
// No permanent node should have been provided.
|
|
CHECK(!other_folder_node);
|
|
@@ -142,6 +146,7 @@ bool BookmarkCodec::Decode(const base::DictValue& 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) {
|
|
if (sync_metadata_str) {
|
|
@@ -154,13 +159,15 @@ bool BookmarkCodec::Decode(const base::DictValue& 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;
|
|
nodes_requiring_id_reassignment_.clear();
|
|
reassigned_ids_per_old_id_.clear();
|
|
|
|
bool success = DecodeHelper(bb_node, other_folder_node, mobile_folder_node,
|
|
+ tabs_folder_node,
|
|
value, sync_metadata_str);
|
|
ReassignIDsIfRequired();
|
|
|
|
@@ -218,6 +225,7 @@ base::DictValue BookmarkCodec::EncodeMetaInfo(
|
|
bool BookmarkCodec::DecodeHelper(BookmarkNode* bb_node,
|
|
BookmarkNode* other_folder_node,
|
|
BookmarkNode* mobile_folder_node,
|
|
+ BookmarkNode* tabs_folder_node,
|
|
const base::DictValue& value,
|
|
std::string* sync_metadata_str) {
|
|
std::optional<int> version = value.FindInt(kVersionKey);
|
|
@@ -240,6 +248,8 @@ bool BookmarkCodec::DecodeHelper(BookmarkNode* bb_node,
|
|
roots->FindDict(kOtherBookmarkFolderNameKey);
|
|
const base::DictValue* mobile_folder_value =
|
|
roots->FindDict(kMobileBookmarkFolderNameKey);
|
|
+ const base::DictValue* tabs_folder_value =
|
|
+ roots->FindDict(kTabsBookmarkFolderNameKey);
|
|
|
|
if (!bb_value || !other_folder_value || !mobile_folder_value)
|
|
return false;
|
|
@@ -247,6 +257,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);
|
|
|
|
// Need to reset the title as the title is persisted and restored from
|
|
// the file.
|
|
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
|
|
@@ -43,6 +43,7 @@ class BookmarkCodec {
|
|
base::DictValue Encode(const BookmarkNode* bookmark_bar_node,
|
|
const BookmarkNode* other_folder_node,
|
|
const BookmarkNode* mobile_folder_node,
|
|
+ const BookmarkNode* tabs_folder_node,
|
|
std::string sync_metadata_str);
|
|
|
|
// Decodes the previously encoded value to the specified nodes as well as
|
|
@@ -61,6 +62,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);
|
|
|
|
@@ -94,6 +96,7 @@ class BookmarkCodec {
|
|
static const char kBookmarkBarFolderNameKey[];
|
|
static const char kOtherBookmarkFolderNameKey[];
|
|
static const char kMobileBookmarkFolderNameKey[];
|
|
+ static const char kTabsBookmarkFolderNameKey[];
|
|
static const char kVersionKey[];
|
|
static const char kChecksumKey[];
|
|
static const char kChecksumSHA256Key[];
|
|
@@ -127,6 +130,7 @@ class BookmarkCodec {
|
|
bool DecodeHelper(BookmarkNode* bb_node,
|
|
BookmarkNode* other_folder_node,
|
|
BookmarkNode* mobile_folder_node,
|
|
+ BookmarkNode* tabs_folder_node,
|
|
const base::DictValue& value,
|
|
std::string* sync_metadata_str);
|
|
|
|
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
|
|
@@ -17,7 +17,7 @@ namespace {
|
|
|
|
// Number of top-level permanent folders excluding the managed node and account
|
|
// bookmarks.
|
|
-constexpr size_t kNumDefaultTopLevelPermanentFolders = 3u;
|
|
+constexpr size_t kNumDefaultTopLevelPermanentFolders = 4u;
|
|
|
|
void UpdateUserFolderStatsRecursively(const BookmarkNode& node,
|
|
bool top_level,
|
|
@@ -64,6 +64,9 @@ BookmarkLoadDetails::BookmarkLoadDetails()
|
|
mobile_folder_node_ = static_cast<BookmarkPermanentNode*>(
|
|
root_node_->Add(BookmarkPermanentNode::CreateMobileBookmarks(
|
|
/*id=*/0, /*is_account_node=*/false)));
|
|
+ tabs_collection_folder_node_ = static_cast<BookmarkPermanentNode*>(
|
|
+ root_node_->Add(BookmarkPermanentNode::CreateTabsCollectionBookmarks(
|
|
+ /*id=*/0, /*is_account_node=*/false)));
|
|
|
|
// Set the nodes' `date_added` to the same time so that there is no inherent
|
|
// hierarchy in terms of their added time between them. This is relevant for
|
|
@@ -120,6 +123,10 @@ void BookmarkLoadDetails::PopulateNodeIdsForLocalOrSyncablePermanentNodes() {
|
|
if (mobile_folder_node_->id() == 0) {
|
|
mobile_folder_node_->set_id(max_id_++);
|
|
}
|
|
+
|
|
+ if (tabs_collection_folder_node_->id() == 0) {
|
|
+ tabs_collection_folder_node_->set_id(max_id_++);
|
|
+ }
|
|
}
|
|
|
|
void BookmarkLoadDetails::AddManagedNode(
|
|
@@ -132,6 +139,7 @@ void BookmarkLoadDetails::AddManagedNode(
|
|
CHECK_NE(bb_node_->id(), 0);
|
|
CHECK_NE(other_folder_node_->id(), 0);
|
|
CHECK_NE(mobile_folder_node_->id(), 0);
|
|
+ CHECK_NE(tabs_collection_folder_node_->id(), 0);
|
|
|
|
has_managed_node_ = true;
|
|
root_node_->Add(std::move(managed_node));
|
|
@@ -139,8 +147,8 @@ void BookmarkLoadDetails::AddManagedNode(
|
|
|
|
void BookmarkLoadDetails::CreateIndices() {
|
|
local_or_syncable_uuid_index_.insert(root_node_.get());
|
|
- static_assert(kNumDefaultTopLevelPermanentFolders == 3u,
|
|
- "The code below assumes three permanent nodes");
|
|
+ static_assert(kNumDefaultTopLevelPermanentFolders == 4u,
|
|
+ "The code below assumes 4 permanent nodes");
|
|
for (const auto& child : root_node_->children()) {
|
|
if (child.get() == account_bb_node_ ||
|
|
child.get() == account_other_folder_node_ ||
|
|
@@ -159,6 +167,7 @@ void BookmarkLoadDetails::ResetPermanentNodePointers() {
|
|
bb_node_ = nullptr;
|
|
other_folder_node_ = nullptr;
|
|
mobile_folder_node_ = nullptr;
|
|
+ tabs_collection_folder_node_ = nullptr;
|
|
account_bb_node_ = nullptr;
|
|
account_other_folder_node_ = nullptr;
|
|
account_mobile_folder_node_ = nullptr;
|
|
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
|
|
@@ -41,6 +41,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_; }
|
|
|
|
// Account permanent nodes (null unless `AddAccountPermanentNodes()` is
|
|
// called).
|
|
@@ -156,6 +157,7 @@ class BookmarkLoadDetails {
|
|
raw_ptr<BookmarkPermanentNode> bb_node_;
|
|
raw_ptr<BookmarkPermanentNode> other_folder_node_;
|
|
raw_ptr<BookmarkPermanentNode> mobile_folder_node_;
|
|
+ raw_ptr<BookmarkPermanentNode> tabs_collection_folder_node_;
|
|
raw_ptr<BookmarkPermanentNode> account_bb_node_;
|
|
raw_ptr<BookmarkPermanentNode> account_other_folder_node_;
|
|
raw_ptr<BookmarkPermanentNode> account_mobile_folder_node_;
|
|
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
|
|
@@ -946,7 +946,7 @@ bool BookmarkModel::HasUserCreatedBookmarksOrFolders() const {
|
|
(account_bookmark_bar_node_ &&
|
|
!account_bookmark_bar_node_->children().empty()) ||
|
|
(account_other_node_ && !account_other_node_->children().empty()) ||
|
|
- (account_mobile_node_ && !account_mobile_node_->children().empty());
|
|
+ (account_mobile_node_ && !account_mobile_node_->children().empty() && tabs_collection_node_->children().empty());
|
|
}
|
|
|
|
bool BookmarkModel::IsBookmarked(const GURL& url) const {
|
|
@@ -1278,6 +1278,7 @@ void BookmarkModel::DoneLoading(std::unique_ptr<BookmarkLoadDetails> 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();
|
|
|
|
account_bookmark_bar_node_ = details->account_bb_node();
|
|
account_other_node_ = details->account_other_folder_node();
|
|
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
|
|
@@ -149,6 +149,12 @@ class BookmarkModel : public BookmarkUndoProvider,
|
|
// bookmarks in the account storage).
|
|
const BookmarkPermanentNode* account_mobile_node() const;
|
|
|
|
+ // Returns the 'mobile' node. This is NULL until loaded.
|
|
+ const BookmarkPermanentNode* 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_;
|
|
@@ -633,6 +639,8 @@ class BookmarkModel : public BookmarkUndoProvider,
|
|
nullptr;
|
|
raw_ptr<BookmarkPermanentNode, AcrossTasksDanglingUntriaged> mobile_node_ =
|
|
nullptr;
|
|
+ raw_ptr<BookmarkPermanentNode, AcrossTasksDanglingUntriaged> tabs_collection_node_ =
|
|
+ nullptr;
|
|
|
|
// Permanent nodes for account storage.
|
|
raw_ptr<BookmarkPermanentNode> account_bookmark_bar_node_ = nullptr;
|
|
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
|
|
@@ -161,6 +161,8 @@ bool BookmarkPermanentNode::IsTypeVisibleWhenEmpty(Type type) {
|
|
#endif
|
|
|
|
switch (type) {
|
|
+ case bookmarks::BookmarkNode::TABS_COLLECTION:
|
|
+ return !is_desktop;
|
|
case BookmarkNode::URL:
|
|
NOTREACHED();
|
|
case BookmarkNode::FOLDER:
|
|
@@ -213,6 +215,17 @@ BookmarkPermanentNode::CreateMobileBookmarks(int64_t id,
|
|
is_account_node));
|
|
}
|
|
|
|
+// static
|
|
+std::unique_ptr<BookmarkPermanentNode>
|
|
+BookmarkPermanentNode::CreateTabsCollectionBookmarks(int64_t id,
|
|
+ bool is_account_node) {
|
|
+ // 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),
|
|
+ is_account_node));
|
|
+}
|
|
+
|
|
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<BookmarkNode>, public TitledUrlNode {
|
|
FOLDER,
|
|
BOOKMARK_BAR,
|
|
OTHER_NODE,
|
|
+ TABS_COLLECTION,
|
|
MOBILE
|
|
};
|
|
|
|
@@ -249,6 +250,9 @@ class BookmarkPermanentNode : public BookmarkNode {
|
|
static std::unique_ptr<BookmarkPermanentNode> CreateMobileBookmarks(
|
|
int64_t id,
|
|
bool is_account_node);
|
|
+ static std::unique_ptr<BookmarkPermanentNode> CreateTabsCollectionBookmarks(
|
|
+ int64_t id,
|
|
+ bool is_account_node);
|
|
|
|
// Returns whether the permanent node of type `type` should be visible even
|
|
// when it is empty (i.e. no children).
|
|
diff --git a/components/bookmarks/browser/bookmark_storage.cc b/components/bookmarks/browser/bookmark_storage.cc
|
|
--- a/components/bookmarks/browser/bookmark_storage.cc
|
|
+++ b/components/bookmarks/browser/bookmark_storage.cc
|
|
@@ -92,6 +92,7 @@ base::DictValue EncodeModelToDict(
|
|
case BookmarkStorage::kSelectLocalOrSyncableNodes:
|
|
return codec.Encode(
|
|
model->bookmark_bar_node(), model->other_node(), model->mobile_node(),
|
|
+ model->tabs_collection_node(),
|
|
model->client()->EncodeLocalOrSyncableBookmarkSyncMetadata());
|
|
case BookmarkStorage::kSelectAccountNodes:
|
|
// Either all permanent folders or none should exist.
|
|
@@ -108,6 +109,7 @@ base::DictValue EncodeModelToDict(
|
|
return codec.Encode(model->account_bookmark_bar_node(),
|
|
model->account_other_node(),
|
|
model->account_mobile_node(),
|
|
+ nullptr,
|
|
model->client()->EncodeAccountBookmarkSyncMetadata());
|
|
}
|
|
|
|
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
|
|
@@ -255,6 +255,9 @@ std::unique_ptr<BookmarkLoadDetails> LoadBookmarks(
|
|
std::unique_ptr<BookmarkPermanentNode> account_mobile_folder_node =
|
|
BookmarkPermanentNode::CreateMobileBookmarks(0,
|
|
/*is_account_node=*/true);
|
|
+ std::unique_ptr<BookmarkPermanentNode> tabs_collection_folder_node =
|
|
+ BookmarkPermanentNode::CreateTabsCollectionBookmarks(
|
|
+ 0, /*is_account_node=*/true);
|
|
|
|
const base::expected<std::string, metrics::BookmarksFileLoadResult>
|
|
json_string = ReadFile(
|
|
@@ -273,7 +276,8 @@ std::unique_ptr<BookmarkLoadDetails> LoadBookmarks(
|
|
} else if (codec.Decode(*root_dict, /*already_assigned_ids=*/{},
|
|
account_bb_node.get(),
|
|
account_other_folder_node.get(),
|
|
- account_mobile_folder_node.get(), &max_node_id,
|
|
+ account_mobile_folder_node.get(),
|
|
+ tabs_collection_folder_node.get(), &max_node_id,
|
|
&sync_metadata_str)) {
|
|
ids_assigned_to_account_nodes = codec.release_assigned_ids();
|
|
|
|
@@ -360,7 +364,8 @@ std::unique_ptr<BookmarkLoadDetails> LoadBookmarks(
|
|
} else if (codec.Decode(*root_dict,
|
|
std::move(ids_assigned_to_account_nodes),
|
|
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_local_or_syncable_sync_metadata_str(
|
|
std::move(sync_metadata_str));
|
|
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;
|
|
}
|
|
--
|