700 lines
35 KiB
Diff
700 lines
35 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
|
|
|
|
---
|
|
chrome/android/java/res/menu/main_menu.xml | 7 +++
|
|
.../chrome/browser/ChromeTabbedActivity.java | 26 ++++++++
|
|
.../browser/bookmarks/BookmarkBridge.java | 60 +++++++++++++++++++
|
|
.../browser/bookmarks/BookmarkUtils.java | 4 ++
|
|
.../android/bookmarks/bookmark_bridge.cc | 17 ++++++
|
|
.../android/bookmarks/bookmark_bridge.h | 4 ++
|
|
.../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 | 24 ++++++--
|
|
components/bookmarks/browser/bookmark_codec.h | 7 ++-
|
|
.../browser/bookmark_load_details.cc | 4 ++
|
|
.../bookmarks/browser/bookmark_load_details.h | 2 +
|
|
.../bookmarks/browser/bookmark_model.cc | 3 +-
|
|
components/bookmarks/browser/bookmark_model.h | 7 +++
|
|
components/bookmarks/browser/bookmark_node.cc | 15 +++++
|
|
components/bookmarks/browser/bookmark_node.h | 5 ++
|
|
components/bookmarks/browser/model_loader.cc | 3 +-
|
|
.../bookmark_specifics_conversions.cc | 1 +
|
|
21 files changed, 202 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
|
|
@@ -64,6 +64,10 @@
|
|
<item android:id="@+id/all_bookmarks_menu_id"
|
|
android:title="@string/menu_bookmarks"
|
|
android:icon="@drawable/btn_star_filled" />
|
|
+ <item android:id="@+id/bookmark_all_tabs_menu_id"
|
|
+ android:title="@string/menu_bookmark_all_tabs"
|
|
+ android:titleCondensed="@string/menu_bookmark_all_tabs"
|
|
+ android:icon="@drawable/ic_folder_blue_24dp" />
|
|
<item android:id="@+id/recent_tabs_menu_id"
|
|
android:title="@string/menu_recent_tabs"
|
|
android:icon="@drawable/devices_black_24dp" />
|
|
@@ -181,6 +185,9 @@
|
|
<item android:id="@+id/menu_group_tabs"
|
|
android:title="@string/menu_group_tabs"
|
|
android:icon="@drawable/ic_widgets" />
|
|
+ <item android:id="@+id/bookmark_all_tabs_menu_id"
|
|
+ android:title="@string/menu_bookmark_all_tabs"
|
|
+ android:icon="@drawable/ic_folder_blue_24dp" />
|
|
<item android:id="@+id/track_prices_row_menu_id"
|
|
android:title="@string/menu_track_prices"
|
|
android:icon="@drawable/ic_trending_down_black" />
|
|
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
|
|
@@ -69,6 +69,8 @@ import org.chromium.chrome.browser.app.tabmodel.ChromeNextTabPolicySupplier;
|
|
import org.chromium.chrome.browser.app.tabmodel.TabModelOrchestrator;
|
|
import org.chromium.chrome.browser.app.tabmodel.TabWindowManagerSingleton;
|
|
import org.chromium.chrome.browser.app.tabmodel.TabbedModeTabModelOrchestrator;
|
|
+import org.chromium.chrome.browser.bookmarks.BookmarkBridge;
|
|
+import org.chromium.chrome.browser.bookmarks.BookmarkModel;
|
|
import org.chromium.chrome.browser.bookmarks.BookmarkUtils;
|
|
import org.chromium.chrome.browser.browserservices.intents.WebappConstants;
|
|
import org.chromium.chrome.browser.compositor.CompositorViewHolder;
|
|
@@ -2150,6 +2152,8 @@ public class ChromeTabbedActivity extends ChromeActivity<ChromeActivityComponent
|
|
CloseAllTabsDialog.show(this, getModalDialogManagerSupplier(),
|
|
() -> getTabModelSelector().closeAllTabs(), /*isIncognito=*/false);
|
|
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(),
|
|
@@ -2202,6 +2206,28 @@ public class ChromeTabbedActivity extends ChromeActivity<ChromeActivityComponent
|
|
mTabModalHandler.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 BookmarkBridge bridge = mBookmarkBridgeSupplier.get();
|
|
+ final BookmarkModel bookmarkModel = new BookmarkModel();
|
|
+ bookmarkModel.finishLoadingBookmarkModel(() -> {
|
|
+ for (int i = 0; i < tabModel.getCount(); i++) {
|
|
+ Tab tab = tabModel.getTabAt(i);
|
|
+ if (tab.isNativePage()) {
|
|
+ continue;
|
|
+ }
|
|
+ bridge.addToTabsCollection(this, tab);
|
|
+ }
|
|
+ bridge.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.common.primitives.UnsignedLongs;
|
|
import com.google.protobuf.InvalidProtocolBufferException;
|
|
|
|
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;
|
|
@@ -46,6 +47,10 @@ import org.chromium.chrome.browser.subscriptions.CommerceSubscription;
|
|
import org.chromium.chrome.browser.subscriptions.CommerceSubscriptionsServiceFactory;
|
|
import org.chromium.chrome.browser.subscriptions.SubscriptionsManager;
|
|
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.BookmarkType;
|
|
import org.chromium.components.commerce.PriceTracking.ProductPrice;
|
|
@@ -90,6 +95,7 @@ import java.io.File;
|
|
* bookmark model stored in native.
|
|
*/
|
|
public class BookmarkBridge {
|
|
+ private static final String TAG = "BookmarkBridge";
|
|
private final Profile mProfile;
|
|
private boolean mIsDestroyed;
|
|
private boolean mIsDoingExtensiveChanges;
|
|
@@ -633,6 +639,16 @@ public class BookmarkBridge {
|
|
mNativeBookmarkBridge, BookmarkBridge.this);
|
|
}
|
|
|
|
+ /**
|
|
+ * @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.
|
|
*/
|
|
@@ -1358,6 +1374,49 @@ public class BookmarkBridge {
|
|
mNativeBookmarkBridge, BookmarkBridge.this, 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 = getUserBookmarkIdForTab(tab);
|
|
+ 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().getSpec());
|
|
+
|
|
+ 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.
|
|
@@ -1655,6 +1714,7 @@ public class BookmarkBridge {
|
|
void getAllFoldersWithDepths(long nativeBookmarkBridge, BookmarkBridge caller,
|
|
List<BookmarkId> folderList, List<Integer> depthList);
|
|
BookmarkId getRootFolderId(long nativeBookmarkBridge, BookmarkBridge caller);
|
|
+ BookmarkId getTabsCollectionFolderId(long nativeBookmarkBridge, BookmarkBridge caller);
|
|
BookmarkId getMobileFolderId(long nativeBookmarkBridge, BookmarkBridge caller);
|
|
BookmarkId getOtherFolderId(long nativeBookmarkBridge, BookmarkBridge caller);
|
|
BookmarkId getDesktopFolderId(long nativeBookmarkBridge, BookmarkBridge caller);
|
|
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkUtils.java
|
|
--- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkUtils.java
|
|
+++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkUtils.java
|
|
@@ -633,6 +633,7 @@ public class BookmarkUtils {
|
|
List<BookmarkId> topLevelFolders = new ArrayList<>();
|
|
BookmarkId desktopNodeId = bookmarkModel.getDesktopFolderId();
|
|
BookmarkId mobileNodeId = bookmarkModel.getMobileFolderId();
|
|
+ BookmarkId tabsCollectionNodeId = bookmarkModel.getTabsCollectionFolderId();
|
|
BookmarkId othersNodeId = bookmarkModel.getOtherFolderId();
|
|
|
|
List<BookmarkId> specialFoldersIds =
|
|
@@ -658,6 +659,9 @@ public class BookmarkUtils {
|
|
if (bookmarkModel.isFolderVisible(mobileNodeId)) {
|
|
topLevelFolders.add(mobileNodeId);
|
|
}
|
|
+ if (bookmarkModel.isFolderVisible(tabsCollectionNodeId)) {
|
|
+ topLevelFolders.add(tabsCollectionNodeId);
|
|
+ }
|
|
if (bookmarkModel.isFolderVisible(desktopNodeId)) {
|
|
topLevelFolders.add(desktopNodeId);
|
|
}
|
|
diff --git a/chrome/browser/android/bookmarks/bookmark_bridge.cc b/chrome/browser/android/bookmarks/bookmark_bridge.cc
|
|
--- a/chrome/browser/android/bookmarks/bookmark_bridge.cc
|
|
+++ b/chrome/browser/android/bookmarks/bookmark_bridge.cc
|
|
@@ -434,6 +434,11 @@ void BookmarkBridge::GetTopLevelFolderIDs(
|
|
top_level_folders.push_back(node.get());
|
|
}
|
|
|
|
+ for (const auto& node : bookmark_model_->tabs_collection_node()->children()) {
|
|
+ if (node->is_folder())
|
|
+ top_level_folders.push_back(node.get());
|
|
+ }
|
|
+
|
|
for (const auto& node : bookmark_model_->bookmark_bar_node()->children()) {
|
|
if (node->is_folder())
|
|
top_level_folders.push_back(node.get());
|
|
@@ -482,6 +487,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(),
|
|
};
|
|
@@ -536,6 +542,17 @@ ScopedJavaLocalRef<jobject> BookmarkBridge::GetMobileFolderId(
|
|
return folder_id_obj;
|
|
}
|
|
|
|
+ScopedJavaLocalRef<jobject> BookmarkBridge::GetTabsCollectionFolderId(
|
|
+ JNIEnv* env,
|
|
+ const JavaParamRef<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::GetOtherFolderId(
|
|
JNIEnv* env,
|
|
const JavaParamRef<jobject>& obj) {
|
|
diff --git a/chrome/browser/android/bookmarks/bookmark_bridge.h b/chrome/browser/android/bookmarks/bookmark_bridge.h
|
|
--- a/chrome/browser/android/bookmarks/bookmark_bridge.h
|
|
+++ b/chrome/browser/android/bookmarks/bookmark_bridge.h
|
|
@@ -121,6 +121,10 @@ class BookmarkBridge : public bookmarks::BaseBookmarkModelObserver,
|
|
JNIEnv* env,
|
|
const base::android::JavaParamRef<jobject>& obj);
|
|
|
|
+ base::android::ScopedJavaLocalRef<jobject> GetTabsCollectionFolderId(
|
|
+ JNIEnv* env,
|
|
+ const base::android::JavaParamRef<jobject>& obj);
|
|
+
|
|
base::android::ScopedJavaLocalRef<jobject> GetOtherFolderId(
|
|
JNIEnv* env,
|
|
const base::android::JavaParamRef<jobject>& 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,6 +190,8 @@ class Writer : public base::RefCountedThreadSafe<Writer> {
|
|
roots->FindDictKey(BookmarkCodec::kOtherBookmarkFolderNameKey);
|
|
base::Value* mobile_folder_value =
|
|
roots->FindDictKey(BookmarkCodec::kMobileBookmarkFolderNameKey);
|
|
+ base::Value* tabs_collection_value =
|
|
+ roots->FindDictKey(BookmarkCodec::kTabsBookmarkFolderNameKey);
|
|
DCHECK(root_folder_value);
|
|
DCHECK(other_folder_value);
|
|
DCHECK(mobile_folder_value);
|
|
@@ -201,7 +203,9 @@ class Writer : public base::RefCountedThreadSafe<Writer> {
|
|
!WriteNode(*static_cast<base::DictionaryValue*>(other_folder_value),
|
|
BookmarkNode::OTHER_NODE) ||
|
|
!WriteNode(*static_cast<base::DictionaryValue*>(mobile_folder_value),
|
|
- BookmarkNode::MOBILE)) {
|
|
+ BookmarkNode::MOBILE) ||
|
|
+ !WriteNode(*static_cast<base::DictionaryValue*>(tabs_collection_value),
|
|
+ BookmarkNode::TABS_COLLECTION)) {
|
|
NotifyOnFinish(BookmarksExportObserver::Result::kCouldNotWriteNodes);
|
|
return;
|
|
}
|
|
@@ -466,6 +470,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
|
|
@@ -117,6 +117,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
|
|
@@ -3414,6 +3414,9 @@ To change this setting, <ph name="BEGIN_LINK"><resetlink></ph>reset sync<p
|
|
<message name="IDS_MENU_TRACK_PRICES" desc="Menu item for tracking prices on tabs. [CHAR_LIMIT=27]">
|
|
Track prices
|
|
</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>
|
|
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 @@
|
|
<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>
|
|
@@ -34,6 +37,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
|
|
@@ -34,6 +34,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 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(),
|
|
std::move(sync_metadata_str));
|
|
}
|
|
|
|
@@ -83,6 +85,7 @@ base::Value 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,
|
|
std::string sync_metadata_str) {
|
|
ids_reassigned_ = false;
|
|
@@ -104,6 +107,7 @@ base::Value BookmarkCodec::Encode(
|
|
roots.SetKey(kBookmarkBarFolderNameKey, EncodeNode(bookmark_bar_node));
|
|
roots.SetKey(kOtherBookmarkFolderNameKey, EncodeNode(other_folder_node));
|
|
roots.SetKey(kMobileBookmarkFolderNameKey, EncodeNode(mobile_folder_node));
|
|
+ roots.SetKey(kTabsBookmarkFolderNameKey, EncodeNode(tabs_folder_node));
|
|
if (model_meta_info_map)
|
|
roots.SetKey(kMetaInfo, EncodeMetaInfo(*model_meta_info_map));
|
|
FinalizeChecksum();
|
|
@@ -120,6 +124,7 @@ bool BookmarkCodec::Decode(const base::Value& 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();
|
|
@@ -127,7 +132,8 @@ bool BookmarkCodec::Decode(const base::Value& value,
|
|
base::GUID::ParseLowercase(BookmarkNode::kBookmarkBarNodeGuid),
|
|
base::GUID::ParseLowercase(BookmarkNode::kOtherBookmarksNodeGuid),
|
|
base::GUID::ParseLowercase(BookmarkNode::kMobileBookmarksNodeGuid),
|
|
- base::GUID::ParseLowercase(BookmarkNode::kManagedNodeGuid)};
|
|
+ base::GUID::ParseLowercase(BookmarkNode::kManagedNodeGuid),
|
|
+ base::GUID::ParseLowercase(BookmarkNode::kTabsCollectionBookmarksNodeGuid)};
|
|
ids_reassigned_ = false;
|
|
guids_reassigned_ = false;
|
|
ids_valid_ = true;
|
|
@@ -135,12 +141,13 @@ bool BookmarkCodec::Decode(const base::Value& 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;
|
|
}
|
|
@@ -190,6 +197,7 @@ base::Value BookmarkCodec::EncodeMetaInfo(
|
|
bool BookmarkCodec::DecodeHelper(BookmarkNode* bb_node,
|
|
BookmarkNode* other_folder_node,
|
|
BookmarkNode* mobile_folder_node,
|
|
+ BookmarkNode* tabs_folder_node,
|
|
const base::Value& value,
|
|
std::string* sync_metadata_str) {
|
|
if (!value.is_dict())
|
|
@@ -221,6 +229,12 @@ bool BookmarkCodec::DecodeHelper(BookmarkNode* bb_node,
|
|
return false;
|
|
|
|
DecodeNode(*bb_value, nullptr, bb_node);
|
|
+ const base::DictionaryValue* tabs_folder_d_value = nullptr;
|
|
+ const base::Value* tabs_folder_value =
|
|
+ roots->FindDictKey(kTabsBookmarkFolderNameKey);
|
|
+ if (tabs_folder_value && tabs_folder_value->GetAsDictionary(&tabs_folder_d_value))
|
|
+ DecodeNode(*tabs_folder_d_value, nullptr, tabs_folder_node);
|
|
+
|
|
DecodeNode(*other_folder_value, nullptr, other_folder_node);
|
|
DecodeNode(*mobile_folder_value, nullptr, mobile_folder_node);
|
|
|
|
@@ -456,11 +470,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
|
|
@@ -48,6 +48,7 @@ class BookmarkCodec {
|
|
base::Value 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,
|
|
std::string sync_metadata_str);
|
|
|
|
@@ -60,6 +61,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);
|
|
|
|
@@ -105,6 +107,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[];
|
|
|
|
// Possible values for kTypeKey.
|
|
static const char kTypeURL[];
|
|
@@ -121,6 +124,7 @@ class BookmarkCodec {
|
|
bool DecodeHelper(BookmarkNode* bb_node,
|
|
BookmarkNode* other_folder_node,
|
|
BookmarkNode* mobile_folder_node,
|
|
+ BookmarkNode* tabs_folder_node,
|
|
const base::Value& value,
|
|
std::string* sync_metadata_str);
|
|
|
|
@@ -132,7 +136,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
|
|
@@ -36,6 +36,10 @@ BookmarkLoadDetails::BookmarkLoadDetails(BookmarkClient* client)
|
|
root_node_->Add(BookmarkPermanentNode::CreateMobileBookmarks(
|
|
max_id_++,
|
|
client->IsPermanentNodeVisibleWhenEmpty(BookmarkNode::MOBILE))));
|
|
+ tabs_collection_folder_node_ = static_cast<BookmarkPermanentNode*>(
|
|
+ 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
|
|
@@ -43,6 +43,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_; }
|
|
|
|
TitledUrlIndex* index() { return index_.get(); }
|
|
std::unique_ptr<TitledUrlIndex> owned_index() { return std::move(index_); }
|
|
@@ -97,6 +98,7 @@ class BookmarkLoadDetails {
|
|
raw_ptr<BookmarkPermanentNode> bb_node_ = nullptr;
|
|
raw_ptr<BookmarkPermanentNode> other_folder_node_ = nullptr;
|
|
raw_ptr<BookmarkPermanentNode> mobile_folder_node_ = nullptr;
|
|
+ BookmarkPermanentNode* tabs_collection_folder_node_ = nullptr;
|
|
LoadManagedNodeCallback load_managed_node_callback_;
|
|
std::unique_ptr<TitledUrlIndex> index_;
|
|
BookmarkNode::MetaInfoMap model_meta_info_map_;
|
|
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
|
|
@@ -565,7 +565,7 @@ bool BookmarkModel::HasBookmarks() {
|
|
bool BookmarkModel::HasNoUserCreatedBookmarksOrFolders() {
|
|
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) {
|
|
@@ -807,6 +807,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();
|
|
|
|
titled_url_index_->SetNodeSorter(
|
|
std::make_unique<TypedCountSorter>(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
|
|
@@ -121,6 +121,12 @@ class BookmarkModel : 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_;
|
|
@@ -396,6 +402,7 @@ class BookmarkModel : public BookmarkUndoProvider,
|
|
raw_ptr<BookmarkPermanentNode> bookmark_bar_node_ = nullptr;
|
|
raw_ptr<BookmarkPermanentNode> other_node_ = nullptr;
|
|
raw_ptr<BookmarkPermanentNode> mobile_node_ = nullptr;
|
|
+ raw_ptr<BookmarkPermanentNode> 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
|
|
@@ -60,6 +60,10 @@ const char BookmarkNode::kOtherBookmarksNodeGuid[] =
|
|
const char BookmarkNode::kMobileBookmarksNodeGuid[] =
|
|
"4cf2e351-0e85-532b-bb37-df045d8f8d0f";
|
|
|
|
+// static
|
|
+const char BookmarkNode::kTabsCollectionBookmarksNodeGuid[] =
|
|
+ "00000000-0000-4000-a000-000000000006";
|
|
+
|
|
// static
|
|
const char BookmarkNode::kManagedNodeGuid[] =
|
|
"323123f4-9381-5aee-80e6-ea5fca2f7672";
|
|
@@ -225,6 +229,17 @@ BookmarkPermanentNode::CreateMobileBookmarks(int64_t id,
|
|
visible_when_empty));
|
|
}
|
|
|
|
+// static
|
|
+std::unique_ptr<BookmarkPermanentNode>
|
|
+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::GUID::ParseLowercase(kTabsCollectionBookmarksNodeGuid),
|
|
+ l10n_util::GetStringUTF16(IDS_BOOKMARK_BAR_TABS_COLLECTION_FOLDER_NAME),
|
|
+ visible_when_empty));
|
|
+}
|
|
+
|
|
BookmarkPermanentNode::BookmarkPermanentNode(int64_t id,
|
|
Type type,
|
|
const base::GUID& guid,
|
|
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
|
|
};
|
|
|
|
@@ -52,6 +53,7 @@ class BookmarkNode : public ui::TreeNode<BookmarkNode>, public TitledUrlNode {
|
|
static const char kBookmarkBarNodeGuid[];
|
|
static const char kOtherBookmarksNodeGuid[];
|
|
static const char kMobileBookmarksNodeGuid[];
|
|
+ static const char kTabsCollectionBookmarksNodeGuid[];
|
|
static const char kManagedNodeGuid[];
|
|
|
|
// A bug in sync caused some problematic GUIDs to be produced.
|
|
@@ -250,6 +252,9 @@ class BookmarkPermanentNode : public BookmarkNode {
|
|
static std::unique_ptr<BookmarkPermanentNode> CreateMobileBookmarks(
|
|
int64_t id,
|
|
bool visible_when_empty);
|
|
+ static std::unique_ptr<BookmarkPermanentNode> 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/model_loader.cc b/components/bookmarks/browser/model_loader.cc
|
|
--- a/components/bookmarks/browser/model_loader.cc
|
|
+++ b/components/bookmarks/browser/model_loader.cc
|
|
@@ -54,7 +54,8 @@ void LoadBookmarks(const base::FilePath& path,
|
|
std::string sync_metadata_str;
|
|
BookmarkCodec codec;
|
|
codec.Decode(*root, 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
|
|
@@ -419,6 +419,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
|