diff --git a/chrome/browser/pdf/pdf_extension_test.cc b/chrome/browser/pdf/pdf_extension_test.cc
index a807204436e7c..775deff2c1b23 100644
--- a/chrome/browser/pdf/pdf_extension_test.cc
+++ b/chrome/browser/pdf/pdf_extension_test.cc
@@ -104,6 +104,15 @@
 #include "chrome/browser/ui/views/location_bar/zoom_bubble_view.h"
 #endif
 
+#if defined(TOOLKIT_VIEWS) && defined(USE_AURA)
+#include "ui/events/base_event_utils.h"
+#include "ui/events/event.h"
+#include "ui/events/gesture_event_details.h"
+#include "ui/events/types/event_type.h"
+#include "ui/views/touchui/touch_selection_menu_views.h"
+#include "ui/views/widget/any_widget_observer.h"
+#endif  // defined(TOOLKIT_VIEWS) && defined(USE_AURA)
+
 using content::WebContents;
 using extensions::ExtensionsAPIClient;
 using guest_view::GuestViewManager;
@@ -2189,6 +2198,48 @@ IN_PROC_BROWSER_TEST_F(PDFExtensionHitTestTest, ContextMenuCoordinates) {
   // UntrustworthyContextMenuParams.
 }
 
+#if defined(TOOLKIT_VIEWS) && defined(USE_AURA)
+// On text selection, a touch selection menu should pop up. On clicking ellipsis
+// icon on the menu, the context menu should open up.
+IN_PROC_BROWSER_TEST_F(PDFExtensionTest,
+                       ContextMenuOpensFromTouchSelectionMenu) {
+  const GURL url = embedded_test_server()->GetURL("/pdf/text_large.pdf");
+  WebContents* const guest_contents = LoadPdfGetGuestContents(url);
+  ASSERT_TRUE(guest_contents);
+
+  views::NamedWidgetShownWaiter waiter(views::test::AnyWidgetTestPasskey{},
+                                       "TouchSelectionMenuViews");
+  gfx::Point text_selection_position(10, 10);
+  ConvertPageCoordToScreenCoord(guest_contents, &text_selection_position);
+  content::SimulateTouchEventAt(GetActiveWebContents(), ui::ET_TOUCH_PRESSED,
+                                text_selection_position);
+  bool success = false;
+  ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
+      GetActiveWebContents(),
+      "window.addEventListener('message', function(event) {"
+      "  if (event.data.type == 'touchSelectionOccurred')"
+      "    window.domAutomationController.send(true);"
+      "});",
+      &success));
+  ASSERT_TRUE(success);
+  content::SimulateTouchEventAt(GetActiveWebContents(), ui::ET_TOUCH_RELEASED,
+                                text_selection_position);
+  views::Widget* widget = waiter.WaitIfNeededAndGet();
+  ASSERT_TRUE(widget);
+  views::TouchSelectionMenuViews* menu =
+      static_cast<views::TouchSelectionMenuViews*>(widget->GetContentsView());
+  ASSERT_TRUE(menu);
+  views::View* ellipsis_button = menu->GetViewByID(
+      views::TouchSelectionMenuViews::ButtonViewId::kEllipsisButton);
+  ASSERT_TRUE(ellipsis_button);
+  ContextMenuWaiter context_menu_observer;
+  ui::GestureEvent tap(0, 0, 0, ui::EventTimeForNow(),
+                       ui::GestureEventDetails(ui::ET_GESTURE_TAP));
+  ellipsis_button->OnGestureEvent(&tap);
+  context_menu_observer.WaitForMenuOpenAndClose();
+}
+#endif  // defined(TOOLKIT_VIEWS) && defined(USE_AURA)
+
 // The plugin document and the mime handler should both use the same background
 // color.
 IN_PROC_BROWSER_TEST_F(PDFExtensionTest, BackgroundColor) {
diff --git a/chrome/browser/resources/pdf/pdf_viewer.js b/chrome/browser/resources/pdf/pdf_viewer.js
index 296c0fc0ccec1..05faebd067273 100644
--- a/chrome/browser/resources/pdf/pdf_viewer.js
+++ b/chrome/browser/resources/pdf/pdf_viewer.js
@@ -1278,6 +1278,11 @@ export class PDFViewer {
         this.isFormFieldFocused_ =
             /** @type {{ focused: boolean }} */ (data).focused;
         return;
+      case 'touchSelectionOccurred':
+        this.sendScriptingMessage_({
+          type: 'touchSelectionOccurred',
+        });
+        return;
     }
     assertNotReached('Unknown message type received: ' + data.type);
   }
diff --git a/chrome/browser/ui/views/touch_selection_menu_chromeos.cc b/chrome/browser/ui/views/touch_selection_menu_chromeos.cc
index ffc9d189121ba..b4d85f99ade8c 100644
--- a/chrome/browser/ui/views/touch_selection_menu_chromeos.cc
+++ b/chrome/browser/ui/views/touch_selection_menu_chromeos.cc
@@ -51,8 +51,9 @@ void TouchSelectionMenuChromeOS::SetActionsForTesting(
 
 void TouchSelectionMenuChromeOS::CreateButtons() {
   if (action_) {
-    views::LabelButton* button = CreateButton(base::UTF8ToUTF16(action_->title),
-                                              kSmartTextSelectionActionTag);
+    views::LabelButton* button =
+        CreateButton(base::UTF8ToUTF16(action_->title));
+    button->set_tag(kSmartTextSelectionActionTag);
 
     if (action_->bitmap_icon) {
       gfx::ImageSkia original(
diff --git a/chrome/test/data/pdf/text_large.in b/chrome/test/data/pdf/text_large.in
new file mode 100644
index 0000000000000..98caf0078108e
--- /dev/null
+++ b/chrome/test/data/pdf/text_large.in
@@ -0,0 +1,45 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /MediaBox [0 0 612 792]
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Resources <<
+    /Font <<
+      /F1 4 0 R
+    >>
+  >>
+  /Contents 5 0 R
+>>
+endobj
+{{object 4 0}} <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Helvetica
+>>
+endobj
+{{object 5 0}} <<
+  {{streamlen}}
+>>
+stream
+BT
+/F1 100 Tf
+0 706 Td
+(LargeText) Tj
+ET
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
\ No newline at end of file
diff --git a/chrome/test/data/pdf/text_large.pdf b/chrome/test/data/pdf/text_large.pdf
new file mode 100644
index 0000000000000..658036bc765ce
--- /dev/null
+++ b/chrome/test/data/pdf/text_large.pdf
@@ -0,0 +1,57 @@
+%PDF-1.7
+%���
+1 0 obj <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+2 0 obj <<
+  /Type /Pages
+  /MediaBox [0 0 612 792]
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+3 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /Resources <<
+    /Font <<
+      /F1 4 0 R
+    >>
+  >>
+  /Contents 5 0 R
+>>
+endobj
+4 0 obj <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Helvetica
+>>
+endobj
+5 0 obj <<
+  /Length 41
+>>
+stream
+BT
+/F1 100 Tf
+0 706 Td
+(LargeText) Tj
+ET
+endstream
+endobj
+xref
+0 6
+0000000000 65535 f 
+0000000015 00000 n 
+0000000068 00000 n 
+0000000157 00000 n 
+0000000283 00000 n 
+0000000359 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 6
+>>
+startxref
+451
+%%EOF
\ No newline at end of file
diff --git a/content/public/test/browser_test_utils.cc b/content/public/test/browser_test_utils.cc
index fda511a564b46..d5fed68d721bf 100644
--- a/content/public/test/browser_test_utils.cc
+++ b/content/public/test/browser_test_utils.cc
@@ -1147,8 +1147,10 @@ void SimulateTapWithModifiersAt(WebContents* web_contents,
 }
 
 #if defined(USE_AURA)
-void SimulateTouchPressAt(WebContents* web_contents, const gfx::Point& point) {
-  ui::TouchEvent touch(ui::ET_TOUCH_PRESSED, point, base::TimeTicks(),
+void SimulateTouchEventAt(WebContents* web_contents,
+                          ui::EventType event_type,
+                          const gfx::Point& point) {
+  ui::TouchEvent touch(event_type, point, base::TimeTicks(),
                        ui::PointerDetails(ui::EventPointerType::kTouch, 0));
   static_cast<RenderWidgetHostViewAura*>(
       web_contents->GetRenderWidgetHostView())
diff --git a/content/public/test/browser_test_utils.h b/content/public/test/browser_test_utils.h
index fac48d0459e48..73885a067c247 100644
--- a/content/public/test/browser_test_utils.h
+++ b/content/public/test/browser_test_utils.h
@@ -52,6 +52,7 @@
 #include "ui/events/keycodes/dom/dom_code.h"
 #include "ui/events/keycodes/dom/dom_key.h"
 #include "ui/events/keycodes/keyboard_codes.h"
+#include "ui/events/types/event_type.h"
 #include "url/gurl.h"
 
 #if defined(OS_WIN)
@@ -287,8 +288,10 @@ void SimulateTouchGestureAt(WebContents* web_contents,
                             blink::WebInputEvent::Type type);
 
 #if defined(USE_AURA)
-// Generates a TouchStart at |point|.
-void SimulateTouchPressAt(WebContents* web_contents, const gfx::Point& point);
+// Generates a TouchEvent of |event_type| at |point|.
+void SimulateTouchEventAt(WebContents* web_contents,
+                          ui::EventType event_type,
+                          const gfx::Point& point);
 
 void SimulateLongTapAt(WebContents* web_contents, const gfx::Point& point);
 #endif
diff --git a/pdf/out_of_process_instance.cc b/pdf/out_of_process_instance.cc
index 9c44da580b2bd..0ae0e1637bbd4 100644
--- a/pdf/out_of_process_instance.cc
+++ b/pdf/out_of_process_instance.cc
@@ -117,6 +117,8 @@ constexpr char kJSDataToSave[] = "dataToSave";
 constexpr char kJSHasUnsavedChanges[] = "hasUnsavedChanges";
 // Consume save token (Plugin -> Page)
 constexpr char kJSConsumeSaveTokenType[] = "consumeSaveToken";
+// Notify when touch selection occurs (Plugin -> Page)
+constexpr char kJSTouchSelectionOccurredType[] = "touchSelectionOccurred";
 // Go to page (Plugin -> Page)
 constexpr char kJSGoToPageType[] = "goToPage";
 constexpr char kJSPageNumber[] = "page";
@@ -1444,6 +1446,12 @@ void OutOfProcessInstance::NotifySelectedFindResultChanged(
   SelectedFindResultChanged(current_find_index);
 }
 
+void OutOfProcessInstance::NotifyTouchSelectionOccurred() {
+  pp::VarDictionary message;
+  message.Set(kType, kJSTouchSelectionOccurredType);
+  PostMessage(message);
+}
+
 void OutOfProcessInstance::GetDocumentPassword(
     pp::CompletionCallbackWithOutput<pp::Var> callback) {
   if (password_callback_) {
diff --git a/pdf/out_of_process_instance.h b/pdf/out_of_process_instance.h
index db8f4b476e453..e8c00636d7a01 100644
--- a/pdf/out_of_process_instance.h
+++ b/pdf/out_of_process_instance.h
@@ -111,6 +111,7 @@ class OutOfProcessInstance : public pp::Instance,
   void UpdateTickMarks(const std::vector<pp::Rect>& tickmarks) override;
   void NotifyNumberOfFindResultsChanged(int total, bool final_result) override;
   void NotifySelectedFindResultChanged(int current_find_index) override;
+  void NotifyTouchSelectionOccurred() override;
   void GetDocumentPassword(
       pp::CompletionCallbackWithOutput<pp::Var> callback) override;
   void Beep() override;
diff --git a/pdf/pdf_engine.h b/pdf/pdf_engine.h
index 514e82ea49c19..33dbe762bd186 100644
--- a/pdf/pdf_engine.h
+++ b/pdf/pdf_engine.h
@@ -176,6 +176,8 @@ class PDFEngine {
     // Updates the index of the currently selected search item.
     virtual void NotifySelectedFindResultChanged(int current_find_index) {}
 
+    virtual void NotifyTouchSelectionOccurred() {}
+
     // Prompts the user for a password to open this document. The callback is
     // called when the password is retrieved.
     virtual void GetDocumentPassword(
diff --git a/pdf/pdfium/pdfium_engine.cc b/pdf/pdfium/pdfium_engine.cc
index be349bbb95d40..7d29a52bb735a 100644
--- a/pdf/pdfium/pdfium_engine.cc
+++ b/pdf/pdfium/pdfium_engine.cc
@@ -1083,6 +1083,9 @@ void PDFiumEngine::OnMultipleClick(int click_count,
 
   selection_.push_back(PDFiumRange(pages_[page_index].get(), start_index,
                                    end_index - start_index));
+
+  if (handling_long_press_)
+    client_->NotifyTouchSelectionOccurred();
 }
 
 bool PDFiumEngine::OnLeftMouseDown(const pp::MouseInputEvent& event) {
@@ -2330,6 +2333,7 @@ void PDFiumEngine::SetGrayscale(bool grayscale) {
 }
 
 void PDFiumEngine::HandleLongPress(const pp::TouchInputEvent& event) {
+  base::AutoReset<bool> handling_long_press_guard(&handling_long_press_, true);
   pp::FloatPoint fp =
       event.GetTouchByIndex(PP_TOUCHLIST_TYPE_TARGETTOUCHES, 0).position();
   pp::Point point;
diff --git a/pdf/pdfium/pdfium_engine.h b/pdf/pdfium/pdfium_engine.h
index 593a51d318fb6..90a6305333b5a 100644
--- a/pdf/pdfium/pdfium_engine.h
+++ b/pdf/pdfium/pdfium_engine.h
@@ -702,6 +702,9 @@ class PDFiumEngine : public PDFEngine,
   // Timer for touch long press detection.
   base::OneShotTimer touch_timer_;
 
+  // Set to true when handling long touch press.
+  bool handling_long_press_ = false;
+
   // The focus item type for the currently focused object.
   FocusElementType focus_item_type_ = FocusElementType::kNone;
 
diff --git a/ui/views/touchui/touch_selection_menu_views.cc b/ui/views/touchui/touch_selection_menu_views.cc
index 8b014f1386d0c..6cc5d4b6ce49a 100644
--- a/ui/views/touchui/touch_selection_menu_views.cc
+++ b/ui/views/touchui/touch_selection_menu_views.cc
@@ -36,7 +36,6 @@ struct MenuCommand {
 };
 
 constexpr int kSpacingBetweenButtons = 2;
-constexpr int kEllipsesButtonTag = -1;
 
 }  // namespace
 
@@ -126,18 +125,21 @@ void TouchSelectionMenuViews::CreateButtons() {
     if (!client_->IsCommandIdEnabled(command.command_id))
       continue;
 
-    Button* button = CreateButton(l10n_util::GetStringUTF16(command.message_id),
-                                  command.command_id);
+    Button* button =
+        CreateButton(l10n_util::GetStringUTF16(command.message_id));
+    button->set_tag(command.command_id);
     AddChildView(button);
   }
 
-  // Finally, add ellipses button.
-  AddChildView(CreateButton(base::ASCIIToUTF16("..."), kEllipsesButtonTag));
+  // Finally, add ellipsis button.
+  LabelButton* ellipsis_button = CreateButton(base::ASCIIToUTF16("..."));
+  ellipsis_button->SetID(ButtonViewId::kEllipsisButton);
+  AddChildView(ellipsis_button);
   InvalidateLayout();
 }
 
-LabelButton* TouchSelectionMenuViews::CreateButton(const base::string16& title,
-                                                   int tag) {
+LabelButton* TouchSelectionMenuViews::CreateButton(
+    const base::string16& title) {
   base::string16 label =
       gfx::RemoveAcceleratorChar(title, '&', nullptr, nullptr);
   LabelButton* button = new LabelButton(this, label, style::CONTEXT_TOUCH_MENU);
@@ -145,7 +147,6 @@ LabelButton* TouchSelectionMenuViews::CreateButton(const base::string16& title,
   button->SetMinSize(kMenuButtonMinSize);
   button->SetFocusForPlatform();
   button->SetHorizontalAlignment(gfx::ALIGN_CENTER);
-  button->set_tag(tag);
   return button;
 }
 
@@ -180,7 +181,7 @@ void TouchSelectionMenuViews::WindowClosing() {
 void TouchSelectionMenuViews::ButtonPressed(Button* sender,
                                             const ui::Event& event) {
   CloseMenu();
-  if (sender->tag() != kEllipsesButtonTag)
+  if (sender->GetID() != ButtonViewId::kEllipsisButton)
     client_->ExecuteCommand(sender->tag(), event.flags());
   else
     client_->RunContextMenu();
diff --git a/ui/views/touchui/touch_selection_menu_views.h b/ui/views/touchui/touch_selection_menu_views.h
index ad1819ee56a41..77121939b1834 100644
--- a/ui/views/touchui/touch_selection_menu_views.h
+++ b/ui/views/touchui/touch_selection_menu_views.h
@@ -24,6 +24,8 @@ class VIEWS_EXPORT TouchSelectionMenuViews : public BubbleDialogDelegateView,
  public:
   METADATA_HEADER(TouchSelectionMenuViews);
 
+  enum ButtonViewId : int { kEllipsisButton = 1 };
+
   TouchSelectionMenuViews(TouchSelectionMenuRunnerViews* owner,
                           ui::TouchSelectionMenuClient* client,
                           aura::Window* context);
@@ -45,7 +47,7 @@ class VIEWS_EXPORT TouchSelectionMenuViews : public BubbleDialogDelegateView,
   virtual void CreateButtons();
 
   // Helper method to create a single button.
-  LabelButton* CreateButton(const base::string16& title, int tag);
+  LabelButton* CreateButton(const base::string16& title);
 
   // ButtonListener:
   void ButtonPressed(Button* sender, const ui::Event& event) override;