[iOS] Handle BookmarkActivity Within SharingCoordinator

SharingCoordinator will now handle add/edit bookmark commands sent from
the Bookmark Activity, and will make use of the new
BookmarkEditCoordiator to handle showing the Edit Bookmark UI.

(cherry picked from commit b63c5ef2d6e01126b3fe93decf9f0978fb469045)

Bug: 1133294
Change-Id: I7f69f241b49646ffb1853a4a78322f5b941b6cef
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2441957
Commit-Queue: Sebastien Lalancette <[email protected]>
Reviewed-by: Gauthier Ambard <[email protected]>
Cr-Original-Commit-Position: refs/heads/master@{#813754}
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2458879
Reviewed-by: Sebastien Lalancette <[email protected]>
Cr-Commit-Position: refs/branch-heads/4280@{#110}
Cr-Branched-From: ea420fb963f9658c9969b6513c56b8f47efa1a2a-refs/heads/master@{#812852}
diff --git a/ios/chrome/browser/ui/activity_services/activities/bookmark_activity.h b/ios/chrome/browser/ui/activity_services/activities/bookmark_activity.h
index 759729d6..187037d 100644
--- a/ios/chrome/browser/ui/activity_services/activities/bookmark_activity.h
+++ b/ios/chrome/browser/ui/activity_services/activities/bookmark_activity.h
@@ -11,20 +11,21 @@
 class BookmarkModel;
 }
 
-@protocol BrowserCommands;
+@protocol BookmarksCommands;
 class GURL;
 class PrefService;
 
 // Activity that adds the page to bookmarks.
 @interface BookmarkActivity : UIActivity
 
-// Initializes the bookmark activity with the |URL| to check to know if the page
-// is already bookmarked in the |bookmarkModel|. The |handler| is used to add
-// the page to the bookmarks. The |prefService| is used to verify if the user
-// can edit their bookmarks or not.
+// Initializes the bookmark activity with a page's |URL| and |title|. The
+// |bookmarkModel| to verify if the page has already been bookmarked or not. The
+// |handler| is used to add the page to the bookmarks. The |prefService| is used
+// to verify if the user can edit their bookmarks or not.
 - (instancetype)initWithURL:(const GURL&)URL
+                      title:(NSString*)title
               bookmarkModel:(bookmarks::BookmarkModel*)bookmarkModel
-                    handler:(id<BrowserCommands>)handler
+                    handler:(id<BookmarksCommands>)handler
                 prefService:(PrefService*)prefService;
 - (instancetype)init NS_UNAVAILABLE;
 
diff --git a/ios/chrome/browser/ui/activity_services/activities/bookmark_activity.mm b/ios/chrome/browser/ui/activity_services/activities/bookmark_activity.mm
index bc88ae2..5cdc4f7 100644
--- a/ios/chrome/browser/ui/activity_services/activities/bookmark_activity.mm
+++ b/ios/chrome/browser/ui/activity_services/activities/bookmark_activity.mm
@@ -4,16 +4,18 @@
 
 #import "ios/chrome/browser/ui/activity_services/activities/bookmark_activity.h"
 
-#include "base/metrics/user_metrics.h"
-#include "base/metrics/user_metrics_action.h"
-#include "components/bookmarks/browser/bookmark_model.h"
-#include "components/bookmarks/common/bookmark_pref_names.h"
-#include "components/prefs/pref_service.h"
-#include "ios/chrome/browser/policy/policy_features.h"
-#include "ios/chrome/browser/ui/commands/browser_commands.h"
-#include "ios/chrome/grit/ios_strings.h"
-#include "ui/base/l10n/l10n_util_mac.h"
-#include "url/gurl.h"
+#import "base/metrics/user_metrics.h"
+#import "base/metrics/user_metrics_action.h"
+#import "components/bookmarks/browser/bookmark_model.h"
+#import "components/bookmarks/common/bookmark_pref_names.h"
+#import "components/prefs/pref_service.h"
+#import "ios/chrome/browser/policy/policy_features.h"
+#import "ios/chrome/browser/ui/commands/bookmark_page_command.h"
+#import "ios/chrome/browser/ui/commands/bookmarks_commands.h"
+#import "ios/chrome/browser/ui/commands/browser_commands.h"
+#import "ios/chrome/grit/ios_strings.h"
+#import "ui/base/l10n/l10n_util_mac.h"
+#import "url/gurl.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
@@ -30,10 +32,12 @@
 @property(nonatomic, assign) BOOL bookmarked;
 // The bookmark model used to validate if a page was bookmarked.
 @property(nonatomic, assign) bookmarks::BookmarkModel* bookmarkModel;
-// The URL for the activity.
+// The URL of the page to be bookmarked.
 @property(nonatomic, assign) GURL URL;
+// The title of the page to be bookmarked.
+@property(nonatomic, assign) NSString* title;
 // The handler invoked when the activity is performed.
-@property(nonatomic, weak) id<BrowserCommands> handler;
+@property(nonatomic, weak) id<BookmarksCommands> handler;
 // User's preferences service.
 @property(nonatomic, assign) PrefService* prefService;
 @end
@@ -41,12 +45,14 @@
 @implementation BookmarkActivity
 
 - (instancetype)initWithURL:(const GURL&)URL
+                      title:(NSString*)title
               bookmarkModel:(bookmarks::BookmarkModel*)bookmarkModel
-                    handler:(id<BrowserCommands>)handler
+                    handler:(id<BookmarksCommands>)handler
                 prefService:(PrefService*)prefService {
   self = [super init];
   if (self) {
     _URL = URL;
+    _title = title;
     _bookmarkModel = bookmarkModel;
     _handler = handler;
     _prefService = prefService;
@@ -89,7 +95,9 @@
 }
 
 - (void)performActivity {
-  [self.handler bookmarkCurrentPage];
+  BookmarkPageCommand* command =
+      [[BookmarkPageCommand alloc] initWithURL:self.URL title:self.title];
+  [self.handler bookmarkPage:command];
   [self activityDidFinish:YES];
 }
 
diff --git a/ios/chrome/browser/ui/activity_services/activities/bookmark_activity_unittest.mm b/ios/chrome/browser/ui/activity_services/activities/bookmark_activity_unittest.mm
index b2c59b7..3915f201 100644
--- a/ios/chrome/browser/ui/activity_services/activities/bookmark_activity_unittest.mm
+++ b/ios/chrome/browser/ui/activity_services/activities/bookmark_activity_unittest.mm
@@ -12,7 +12,8 @@
 #include "components/prefs/testing_pref_service.h"
 #include "ios/chrome/browser/policy/policy_features.h"
 #include "ios/chrome/browser/ui/bookmarks/bookmark_ios_unittest.h"
-#include "ios/chrome/browser/ui/commands/browser_commands.h"
+#include "ios/chrome/browser/ui/commands/bookmark_page_command.h"
+#include "ios/chrome/browser/ui/commands/bookmarks_commands.h"
 #include "ios/chrome/grit/ios_strings.h"
 #import "third_party/ocmock/OCMock/OCMock.h"
 #include "ui/base/l10n/l10n_util_mac.h"
@@ -22,6 +23,12 @@
 #error "This file requires ARC support."
 #endif
 
+namespace {
+
+NSString* const kTestTitle = @"Test Title";
+
+}  // namespace
+
 // Test fixture for covering the BookmarkActivity class.
 class BookmarkActivityTest : public BookmarkIOSUnitTest {
  protected:
@@ -33,7 +40,7 @@
     // Turn off flag by default.
     scoped_features_.InitAndDisableFeature(kEditBookmarksIOS);
 
-    mocked_handler_ = OCMProtocolMock(@protocol(BrowserCommands));
+    mocked_handler_ = OCMProtocolMock(@protocol(BookmarksCommands));
 
     RegisterPrefs();
   }
@@ -53,6 +60,7 @@
   // Creates a BookmarkActivity instance with the given |URL|.
   BookmarkActivity* CreateActivity(const GURL& URL) {
     return [[BookmarkActivity alloc] initWithURL:URL
+                                           title:kTestTitle
                                    bookmarkModel:bookmark_model_
                                          handler:mocked_handler_
                                      prefService:&testing_pref_service_];
@@ -97,6 +105,7 @@
 TEST_F(BookmarkActivityTest, NilBookmarkModel_NoCrash) {
   BookmarkActivity* activity =
       [[BookmarkActivity alloc] initWithURL:GURL("https://example.com/")
+                                      title:kTestTitle
                               bookmarkModel:nil
                                     handler:mocked_handler_
                                 prefService:&testing_pref_service_];
@@ -127,3 +136,23 @@
       l10n_util::GetNSString(IDS_IOS_TOOLS_MENU_EDIT_BOOKMARK);
   EXPECT_TRUE([editBookmarkString isEqualToString:activity.activityTitle]);
 }
+
+TEST_F(BookmarkActivityTest, PerformActivity_BookmarkPageCommand) {
+  GURL testUrl("https://example.com/");
+  BookmarkActivity* activity = CreateActivity(testUrl);
+
+  [[mocked_handler_ expect]
+      bookmarkPage:[OCMArg checkWithBlock:^BOOL(BookmarkPageCommand* value) {
+        EXPECT_EQ(testUrl, value.URL);
+        EXPECT_EQ(kTestTitle, value.title);
+        return YES;
+      }]];
+
+  id activity_partial_mock = OCMPartialMock(activity);
+  [[activity_partial_mock expect] activityDidFinish:YES];
+
+  [activity performActivity];
+
+  [mocked_handler_ verify];
+  [activity_partial_mock verify];
+}
diff --git a/ios/chrome/browser/ui/activity_services/activity_service_coordinator.h b/ios/chrome/browser/ui/activity_services/activity_service_coordinator.h
index 052e92a..455d61b 100644
--- a/ios/chrome/browser/ui/activity_services/activity_service_coordinator.h
+++ b/ios/chrome/browser/ui/activity_services/activity_service_coordinator.h
@@ -11,6 +11,7 @@
 @class ActivityParams;
 @protocol ActivityServicePositioner;
 @protocol ActivityServicePresentation;
+@protocol BookmarksCommands;
 class Browser;
 @protocol QRGenerationCommands;
 
@@ -38,7 +39,9 @@
     presentationProvider;
 
 // Handler for activities that need to be executed within a certain scope.
-@property(nonatomic, readwrite, weak) id<QRGenerationCommands> scopedHandler;
+@property(nonatomic, readwrite, weak)
+    id<BookmarksCommands, QRGenerationCommands>
+        scopedHandler;
 
 @end
 
diff --git a/ios/chrome/browser/ui/activity_services/activity_service_coordinator.mm b/ios/chrome/browser/ui/activity_services/activity_service_coordinator.mm
index 2744c3a..a74d857 100644
--- a/ios/chrome/browser/ui/activity_services/activity_service_coordinator.mm
+++ b/ios/chrome/browser/ui/activity_services/activity_service_coordinator.mm
@@ -21,6 +21,7 @@
 #import "ios/chrome/browser/ui/activity_services/data/share_to_data_builder.h"
 #import "ios/chrome/browser/ui/activity_services/requirements/activity_service_positioner.h"
 #import "ios/chrome/browser/ui/activity_services/requirements/activity_service_presentation.h"
+#import "ios/chrome/browser/ui/commands/bookmarks_commands.h"
 #import "ios/chrome/browser/ui/commands/command_dispatcher.h"
 #import "ios/chrome/browser/ui/util/uikit_ui_util.h"
 #import "ios/chrome/browser/web_state_list/web_state_list.h"
@@ -78,6 +79,7 @@
       ios::BookmarkModelFactory::GetForBrowserState(browserState);
   self.mediator =
       [[ActivityServiceMediator alloc] initWithHandler:self.handler
+                                      bookmarksHandler:self.scopedHandler
                                    qrGenerationHandler:self.scopedHandler
                                            prefService:browserState->GetPrefs()
                                          bookmarkModel:bookmarkModel];
diff --git a/ios/chrome/browser/ui/activity_services/activity_service_mediator.h b/ios/chrome/browser/ui/activity_services/activity_service_mediator.h
index 8d9af44..9927dee4b 100644
--- a/ios/chrome/browser/ui/activity_services/activity_service_mediator.h
+++ b/ios/chrome/browser/ui/activity_services/activity_service_mediator.h
@@ -14,6 +14,7 @@
 class BookmarkModel;
 }
 
+@protocol BookmarksCommands;
 @protocol BrowserCommands;
 @class ChromeActivityImageSource;
 @protocol ChromeActivityItemSource;
@@ -28,10 +29,12 @@
 @interface ActivityServiceMediator : NSObject
 
 // Initializes a mediator instance with a |handler| used to execute action, a
+// |bookmarksHandler| to execute Bookmarks actions, a
 // |qrGenerationHandler| to execute QR generation actions, a |prefService| to
 // read settings and policies, and a |bookmarkModel| to retrieve bookmark
 // states.
 - (instancetype)initWithHandler:(id<BrowserCommands, FindInPageCommands>)handler
+               bookmarksHandler:(id<BookmarksCommands>)bookmarksHandler
             qrGenerationHandler:(id<QRGenerationCommands>)qrGenerationHandler
                     prefService:(PrefService*)prefService
                   bookmarkModel:(bookmarks::BookmarkModel*)bookmarkModel
diff --git a/ios/chrome/browser/ui/activity_services/activity_service_mediator.mm b/ios/chrome/browser/ui/activity_services/activity_service_mediator.mm
index 74ea239c..18be086 100644
--- a/ios/chrome/browser/ui/activity_services/activity_service_mediator.mm
+++ b/ios/chrome/browser/ui/activity_services/activity_service_mediator.mm
@@ -31,6 +31,7 @@
 #import "ios/chrome/browser/ui/activity_services/data/share_image_data.h"
 #import "ios/chrome/browser/ui/activity_services/data/share_to_data.h"
 #import "ios/chrome/browser/ui/activity_services/requirements/activity_service_positioner.h"
+#import "ios/chrome/browser/ui/commands/bookmarks_commands.h"
 #import "ios/chrome/browser/ui/commands/qr_generation_commands.h"
 #import "ios/chrome/browser/ui/util/uikit_ui_util.h"
 
@@ -42,6 +43,8 @@
 
 @property(nonatomic, weak) id<BrowserCommands, FindInPageCommands> handler;
 
+@property(nonatomic, weak) id<BookmarksCommands> bookmarksHandler;
+
 @property(nonatomic, weak) id<QRGenerationCommands> qrGenerationHandler;
 
 @property(nonatomic, assign) PrefService* prefService;
@@ -55,11 +58,13 @@
 #pragma mark - Public
 
 - (instancetype)initWithHandler:(id<BrowserCommands, FindInPageCommands>)handler
+               bookmarksHandler:(id<BookmarksCommands>)bookmarksHandler
             qrGenerationHandler:(id<QRGenerationCommands>)qrGenerationHandler
                     prefService:(PrefService*)prefService
                   bookmarkModel:(bookmarks::BookmarkModel*)bookmarkModel {
   if (self = [super init]) {
     _handler = handler;
+    _bookmarksHandler = bookmarksHandler;
     _qrGenerationHandler = qrGenerationHandler;
     _prefService = prefService;
     _bookmarkModel = bookmarkModel;
@@ -96,8 +101,9 @@
 
     BookmarkActivity* bookmarkActivity =
         [[BookmarkActivity alloc] initWithURL:data.visibleURL
+                                        title:data.title
                                 bookmarkModel:self.bookmarkModel
-                                      handler:self.handler
+                                      handler:self.bookmarksHandler
                                   prefService:self.prefService];
     [applicationActivities addObject:bookmarkActivity];
 
diff --git a/ios/chrome/browser/ui/activity_services/activity_service_mediator_unittest.mm b/ios/chrome/browser/ui/activity_services/activity_service_mediator_unittest.mm
index 466c551..e95c1c0 100644
--- a/ios/chrome/browser/ui/activity_services/activity_service_mediator_unittest.mm
+++ b/ios/chrome/browser/ui/activity_services/activity_service_mediator_unittest.mm
@@ -25,6 +25,7 @@
 #import "ios/chrome/browser/ui/activity_services/data/chrome_activity_url_source.h"
 #import "ios/chrome/browser/ui/activity_services/data/share_image_data.h"
 #import "ios/chrome/browser/ui/activity_services/data/share_to_data.h"
+#import "ios/chrome/browser/ui/commands/bookmarks_commands.h"
 #import "ios/chrome/browser/ui/commands/browser_commands.h"
 #import "ios/chrome/browser/ui/commands/find_in_page_commands.h"
 #import "ios/chrome/browser/ui/commands/qr_generation_commands.h"
@@ -48,6 +49,8 @@
     pref_service_ = std::make_unique<TestingPrefServiceSimple>();
 
     mocked_handler_ = OCMStrictProtocolMock(@protocol(HandlerProtocols));
+    mocked_bookmarks_handler_ =
+        OCMStrictProtocolMock(@protocol(BookmarksCommands));
     mocked_qr_generation_handler_ =
         OCMStrictProtocolMock(@protocol(QRGenerationCommands));
     mocked_thumbnail_generator_ =
@@ -55,6 +58,7 @@
 
     mediator_ = [[ActivityServiceMediator alloc]
             initWithHandler:mocked_handler_
+           bookmarksHandler:mocked_bookmarks_handler_
         qrGenerationHandler:mocked_qr_generation_handler_
                 prefService:pref_service_.get()
               bookmarkModel:nil];
@@ -71,6 +75,7 @@
   }
 
   id mocked_handler_;
+  id mocked_bookmarks_handler_;
   id mocked_qr_generation_handler_;
   id mocked_thumbnail_generator_;
   std::unique_ptr<TestingPrefServiceSimple> pref_service_;
diff --git a/ios/chrome/browser/ui/sharing/BUILD.gn b/ios/chrome/browser/ui/sharing/BUILD.gn
index 2c23fdf..89d56be 100644
--- a/ios/chrome/browser/ui/sharing/BUILD.gn
+++ b/ios/chrome/browser/ui/sharing/BUILD.gn
@@ -10,11 +10,17 @@
   ]
   deps = [
     "//base",
+    "//components/bookmarks/browser",
+    "//ios/chrome/browser/bookmarks",
+    "//ios/chrome/browser/main:public",
     "//ios/chrome/browser/ui/activity_services",
     "//ios/chrome/browser/ui/activity_services/requirements",
+    "//ios/chrome/browser/ui/bookmarks:core",
+    "//ios/chrome/browser/ui/bookmarks:edit",
     "//ios/chrome/browser/ui/commands",
     "//ios/chrome/browser/ui/coordinators:chrome_coordinators",
     "//ios/chrome/browser/ui/qr_generator",
+    "//ios/third_party/material_components_ios",
     "//url",
   ]
 }
@@ -27,13 +33,21 @@
     ":sharing",
     "//base",
     "//base/test:test_support",
+    "//components/bookmarks/browser",
+    "//components/bookmarks/test",
+    "//ios/chrome/browser/bookmarks",
     "//ios/chrome/browser/browser_state",
     "//ios/chrome/browser/main:test_support",
     "//ios/chrome/browser/ui/activity_services",
     "//ios/chrome/browser/ui/activity_services/requirements",
+    "//ios/chrome/browser/ui/bookmarks:bookmarks_ui",
+    "//ios/chrome/browser/ui/bookmarks:edit",
+    "//ios/chrome/browser/ui/bookmarks:test_support",
     "//ios/chrome/browser/ui/commands",
+    "//ios/chrome/browser/ui/table_view",
     "//ios/chrome/browser/web_state_list",
     "//ios/chrome/test:test_support",
+    "//ios/third_party/material_components_ios",
     "//ios/web/public",
     "//ios/web/public/test",
     "//ios/web/public/test/fakes",
diff --git a/ios/chrome/browser/ui/sharing/sharing_coordinator.mm b/ios/chrome/browser/ui/sharing/sharing_coordinator.mm
index 39b4cbf..dd08070c 100644
--- a/ios/chrome/browser/ui/sharing/sharing_coordinator.mm
+++ b/ios/chrome/browser/ui/sharing/sharing_coordinator.mm
@@ -4,24 +4,42 @@
 
 #import "ios/chrome/browser/ui/sharing/sharing_coordinator.h"
 
+#import <MaterialComponents/MaterialSnackbar.h>
+
+#import "base/ios/block_types.h"
+#import "components/bookmarks/browser/bookmark_model.h"
+#import "ios/chrome/browser/bookmarks/bookmark_model_factory.h"
+#import "ios/chrome/browser/main/browser.h"
 #import "ios/chrome/browser/ui/activity_services/activity_params.h"
 #import "ios/chrome/browser/ui/activity_services/activity_service_coordinator.h"
 #import "ios/chrome/browser/ui/activity_services/requirements/activity_service_positioner.h"
 #import "ios/chrome/browser/ui/activity_services/requirements/activity_service_presentation.h"
+#import "ios/chrome/browser/ui/bookmarks/bookmark_edit_coordinator.h"
+#import "ios/chrome/browser/ui/bookmarks/bookmark_mediator.h"
+#import "ios/chrome/browser/ui/commands/bookmark_page_command.h"
+#import "ios/chrome/browser/ui/commands/bookmarks_commands.h"
+#import "ios/chrome/browser/ui/commands/command_dispatcher.h"
 #import "ios/chrome/browser/ui/commands/qr_generation_commands.h"
+#import "ios/chrome/browser/ui/commands/snackbar_commands.h"
 #import "ios/chrome/browser/ui/qr_generator/qr_generator_coordinator.h"
-#include "url/gurl.h"
+#import "url/gurl.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
 #endif
 
+using bookmarks::BookmarkNode;
+
 @interface SharingCoordinator () <ActivityServicePositioner,
                                   ActivityServicePresentation,
+                                  BookmarkEditCoordinatorDelegate,
+                                  BookmarksCommands,
                                   QRGenerationCommands>
 @property(nonatomic, strong)
     ActivityServiceCoordinator* activityServiceCoordinator;
 
+@property(nonatomic, strong) BookmarkEditCoordinator* bookmarkEditCoordinator;
+
 @property(nonatomic, strong) QRGeneratorCoordinator* qrGeneratorCoordinator;
 
 @property(nonatomic, strong) ActivityParams* params;
@@ -62,6 +80,7 @@
 
 - (void)stop {
   [self activityServiceDidEndPresenting];
+  [self bookmarkEditDismissed:self.bookmarkEditCoordinator];
   [self hideQRCode];
   self.originView = nil;
 }
@@ -79,6 +98,61 @@
   self.activityServiceCoordinator = nil;
 }
 
+#pragma mark - BookmarksCommands
+
+- (void)bookmarkPage:(BookmarkPageCommand*)command {
+  DCHECK(command);
+  DCHECK(command.URL.is_valid());
+
+  ChromeBrowserState* browserState = self.browser->GetBrowserState();
+  bookmarks::BookmarkModel* bookmarkModel =
+      ios::BookmarkModelFactory::GetForBrowserState(browserState);
+  if (!bookmarkModel && bookmarkModel->loaded()) {
+    return;
+  }
+
+  const BookmarkNode* node =
+      bookmarkModel->GetMostRecentlyAddedUserNodeForURL(command.URL);
+  if (node) {
+    // Trigger the Edit bookmark scenario.
+    [self editBookmark:node];
+  } else {
+    // Add the bookmark and show a snackbar message.
+    id<SnackbarCommands> handler = HandlerForProtocol(
+        self.browser->GetCommandDispatcher(), SnackbarCommands);
+    BookmarkMediator* mediator = [[BookmarkMediator alloc]
+        initWithBrowserState:self.browser->GetBrowserState()];
+
+    __weak __typeof(self) weakSelf = self;
+    ProceduralBlock editAction = ^{
+      if (!weakSelf || !bookmarkModel) {
+        return;
+      }
+
+      const BookmarkNode* newNode =
+          bookmarkModel->GetMostRecentlyAddedUserNodeForURL(command.URL);
+      DCHECK(newNode);
+      if (newNode) {
+        [weakSelf editBookmark:newNode];
+      }
+    };
+
+    MDCSnackbarMessage* snackbarMessage =
+        [mediator addBookmarkWithTitle:command.title
+                                   URL:command.URL
+                            editAction:editAction];
+    [handler showSnackbarMessage:snackbarMessage];
+  }
+}
+
+#pragma mark - BookmarkEditCoordinatorDelegate
+
+- (void)bookmarkEditDismissed:(BookmarkEditCoordinator*)coordinator {
+  DCHECK(self.bookmarkEditCoordinator == coordinator);
+  [self.bookmarkEditCoordinator stop];
+  self.bookmarkEditCoordinator = nil;
+}
+
 #pragma mark - QRGenerationCommands
 
 - (void)generateQRCode:(GenerateQRCodeCommand*)command {
@@ -96,4 +170,16 @@
   self.qrGeneratorCoordinator = nil;
 }
 
+#pragma mark - Private Methods
+
+- (void)editBookmark:(const BookmarkNode*)bookmark {
+  self.bookmarkEditCoordinator = [[BookmarkEditCoordinator alloc]
+      initWithBaseViewController:self.baseViewController
+                         browser:self.browser
+                        bookmark:bookmark];
+  self.bookmarkEditCoordinator.delegate = self;
+
+  [self.bookmarkEditCoordinator start];
+}
+
 @end
diff --git a/ios/chrome/browser/ui/sharing/sharing_coordinator_unittest.mm b/ios/chrome/browser/ui/sharing/sharing_coordinator_unittest.mm
index eaa7b00d..0abcf45 100644
--- a/ios/chrome/browser/ui/sharing/sharing_coordinator_unittest.mm
+++ b/ios/chrome/browser/ui/sharing/sharing_coordinator_unittest.mm
@@ -4,36 +4,53 @@
 
 #import "ios/chrome/browser/ui/sharing/sharing_coordinator.h"
 
+#import <MaterialComponents/MaterialSnackbar.h>
 #import <UIKit/UIKit.h>
 
-#include "base/values.h"
-#include "ios/chrome/browser/browser_state/chrome_browser_state.h"
-#include "ios/chrome/browser/main/test_browser.h"
+#import "base/ios/block_types.h"
+#import "base/strings/sys_string_conversions.h"
+#import "base/values.h"
+#import "components/bookmarks/browser/bookmark_model.h"
+#import "components/bookmarks/browser/bookmark_node.h"
+#import "components/bookmarks/test/bookmark_test_helpers.h"
+#import "ios/chrome/browser/bookmarks/bookmark_model_factory.h"
+#import "ios/chrome/browser/browser_state/chrome_browser_state.h"
+#import "ios/chrome/browser/main/test_browser.h"
 #import "ios/chrome/browser/ui/activity_services/activity_params.h"
 #import "ios/chrome/browser/ui/activity_services/requirements/activity_service_positioner.h"
 #import "ios/chrome/browser/ui/activity_services/requirements/activity_service_presentation.h"
+#import "ios/chrome/browser/ui/bookmarks/bookmark_edit_coordinator.h"
+#import "ios/chrome/browser/ui/bookmarks/bookmark_edit_view_controller.h"
+#import "ios/chrome/browser/ui/bookmarks/bookmark_ios_unittest.h"
+#import "ios/chrome/browser/ui/commands/bookmark_page_command.h"
+#import "ios/chrome/browser/ui/commands/bookmarks_commands.h"
 #import "ios/chrome/browser/ui/commands/command_dispatcher.h"
 #import "ios/chrome/browser/ui/commands/generate_qr_code_command.h"
 #import "ios/chrome/browser/ui/commands/qr_generation_commands.h"
+#import "ios/chrome/browser/ui/commands/snackbar_commands.h"
+#import "ios/chrome/browser/ui/table_view/table_view_navigation_controller.h"
 #import "ios/chrome/browser/web_state_list/web_state_list.h"
 #import "ios/chrome/browser/web_state_list/web_state_opener.h"
 #import "ios/chrome/test/scoped_key_window.h"
 #import "ios/web/public/test/fakes/test_navigation_manager.h"
 #import "ios/web/public/test/fakes/test_web_state.h"
-#include "ios/web/public/test/web_task_environment.h"
+#import "ios/web/public/test/web_task_environment.h"
 #import "ios/web/public/web_state.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "testing/gtest_mac.h"
-#include "testing/platform_test.h"
+#import "testing/gmock/include/gmock/gmock.h"
+#import "testing/gtest/include/gtest/gtest.h"
+#import "testing/gtest_mac.h"
+#import "testing/platform_test.h"
 #import "third_party/ocmock/OCMock/OCMock.h"
-#include "third_party/ocmock/gtest_support.h"
-#include "url/gurl.h"
+#import "third_party/ocmock/gtest_support.h"
+#import "url/gurl.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
 #endif
 
+using bookmarks::BookmarkModel;
+using bookmarks::BookmarkNode;
+
 class MockTestWebState : public web::TestWebState {
  public:
   MockTestWebState() : web::TestWebState() {
@@ -45,27 +62,72 @@
 };
 
 // Test fixture for testing SharingCoordinator.
-class SharingCoordinatorTest : public PlatformTest {
+class SharingCoordinatorTest : public BookmarkIOSUnitTest {
  protected:
   SharingCoordinatorTest()
       : base_view_controller_([[UIViewController alloc] init]),
-        browser_(std::make_unique<TestBrowser>()),
         fake_origin_view_([[UIView alloc] init]),
         test_scenario_(ActivityScenario::TabShareButton) {
     [scoped_key_window_.Get() setRootViewController:base_view_controller_];
   }
 
+  void SetUp() override {
+    BookmarkIOSUnitTest::SetUp();
+    snackbar_handler_ = OCMStrictProtocolMock(@protocol(SnackbarCommands));
+    [browser_->GetCommandDispatcher()
+        startDispatchingToTarget:snackbar_handler_
+                     forProtocol:@protocol(SnackbarCommands)];
+  }
+
   void AppendNewWebState(std::unique_ptr<web::TestWebState> web_state) {
     browser_->GetWebStateList()->InsertWebState(
         WebStateList::kInvalidIndex, std::move(web_state),
         WebStateList::INSERT_ACTIVATE, WebStateOpener());
   }
 
-  web::WebTaskEnvironment task_environment_;
+  // Validates that |trigger_block| gets a Edit Bookmark VC to be presented,
+  // and that |delegate| controls its dismissal.
+  void ValidateEditBookmark(ProceduralBlock trigger_block,
+                            id<BookmarkEditCoordinatorDelegate> delegate) {
+    id vc_partial_mock = OCMPartialMock(base_view_controller_);
+    __block BookmarkEditViewController* bookmarkEditVC;
+    [[vc_partial_mock expect]
+        presentViewController:[OCMArg checkWithBlock:^BOOL(
+                                          UIViewController* viewController) {
+          if ([viewController
+                  isKindOfClass:[TableViewNavigationController class]]) {
+            TableViewNavigationController* navController =
+                (TableViewNavigationController*)viewController;
+            if ([navController.tableViewController
+                    isKindOfClass:[BookmarkEditViewController class]]) {
+              bookmarkEditVC = (BookmarkEditViewController*)
+                                   navController.tableViewController;
+              return YES;
+            }
+            return NO;
+          }
+          return NO;
+        }]
+                     animated:YES
+                   completion:nil];
+
+    trigger_block();
+
+    [vc_partial_mock verify];
+
+    [[vc_partial_mock expect] dismissViewControllerAnimated:YES completion:nil];
+
+    // Dismiss the ViewController.
+    ASSERT_NE(nil, bookmarkEditVC);
+    [bookmarkEditVC dismiss];
+
+    [vc_partial_mock verify];
+  }
+
   ScopedKeyWindow scoped_key_window_;
   UIViewController* base_view_controller_;
-  std::unique_ptr<TestBrowser> browser_;
   UIView* fake_origin_view_;
+  id snackbar_handler_;
   ActivityScenario test_scenario_;
 };
 
@@ -196,3 +258,87 @@
 
   [vc_partial_mock verify];
 }
+
+// Tests that the coordinator can handle adding a new bookmark, and the edit
+// action is hooked-up properly.
+TEST_F(SharingCoordinatorTest, AddBookmark_EditViaSnackbar) {
+  @autoreleasepool {
+    ActivityParams* params =
+        [[ActivityParams alloc] initWithScenario:test_scenario_];
+    SharingCoordinator* coordinator = [[SharingCoordinator alloc]
+        initWithBaseViewController:base_view_controller_
+                           browser:browser_.get()
+                            params:params
+                        originView:fake_origin_view_];
+
+    __block ProceduralBlock edit_action = nil;
+    [[snackbar_handler_ expect]
+        showSnackbarMessage:[OCMArg checkWithBlock:^BOOL(
+                                        MDCSnackbarMessage* message) {
+          edit_action = message.action.handler;
+          return YES;
+        }]];
+
+    GURL test_url("https://wwww.chromium.org");
+    NSString* test_title = @"Test Title";
+    BookmarkPageCommand* command =
+        [[BookmarkPageCommand alloc] initWithURL:test_url title:test_title];
+
+    ASSERT_EQ(nil,
+              bookmark_model_->GetMostRecentlyAddedUserNodeForURL(command.URL));
+
+    auto handler = static_cast<id<BookmarksCommands>>(coordinator);
+    [handler bookmarkPage:command];
+
+    const BookmarkNode* bookmark =
+        bookmark_model_->GetMostRecentlyAddedUserNodeForURL(command.URL);
+
+    ASSERT_NE(nil, bookmark);
+    EXPECT_EQ(test_url, bookmark->url());
+    EXPECT_EQ(base::SysNSStringToUTF16(test_title), bookmark->GetTitle());
+
+    [snackbar_handler_ verify];
+    ASSERT_NE(nil, edit_action);
+
+    // Verify snackbar message's Edit action.
+    auto bookmark_delegate =
+        static_cast<id<BookmarkEditCoordinatorDelegate>>(coordinator);
+
+    ValidateEditBookmark(edit_action, bookmark_delegate);
+  }
+}
+
+// Tests that the coordinator can handle editing an existing bookmark via the
+// bookmarkPage command.
+TEST_F(SharingCoordinatorTest, EditExistingBookmark) {
+  @autoreleasepool {
+    ActivityParams* params =
+        [[ActivityParams alloc] initWithScenario:test_scenario_];
+    SharingCoordinator* coordinator = [[SharingCoordinator alloc]
+        initWithBaseViewController:base_view_controller_
+                           browser:browser_.get()
+                            params:params
+                        originView:fake_origin_view_];
+
+    const BookmarkNode* bookmark =
+        AddBookmark(bookmark_model_->mobile_node(), @"Some Other Title");
+
+    NSString* test_title = @"Test Title";
+    BookmarkPageCommand* command =
+        [[BookmarkPageCommand alloc] initWithURL:bookmark->url()
+                                           title:test_title];
+
+    ASSERT_EQ(bookmark,
+              bookmark_model_->GetMostRecentlyAddedUserNodeForURL(command.URL));
+
+    auto handler = static_cast<id<BookmarksCommands>>(coordinator);
+
+    ProceduralBlock trigger = ^{
+      [handler bookmarkPage:command];
+    };
+    auto bookmark_delegate =
+        static_cast<id<BookmarkEditCoordinatorDelegate>>(coordinator);
+
+    ValidateEditBookmark(trigger, bookmark_delegate);
+  }
+}