Add PDFExtensionTest for opening context menu from touch selection menu
This CL adds a browser test for TouchSelectionMenu in PDF. There is no utility class which could help in determining when touch selection menu is opened. This CL adds TouchSelectionMenuWaiter() to determine when touch selection menu is shown and helps in executing commands on the touch selection menu. Fix some nits in TouchSelectionMenuViews along the way. Bug: 1027842 Change-Id: I9ce95ab2b697d474fedddec2777f75252f520d6f Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2082750 Reviewed-by: Lei Zhang <thestig@chromium.org> Reviewed-by: Alex Moshchuk <alexmos@chromium.org> Reviewed-by: Peter Kasting <pkasting@chromium.org> Commit-Queue: Ankit Kumar 🌪️ <ankk@microsoft.com> Cr-Commit-Position: refs/heads/master@{#764914}
This commit is contained in:
chrome
browser
test
data
content/public/test
pdf
ui/views/touchui
@ -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) {
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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(
|
||||
|
45
chrome/test/data/pdf/text_large.in
Normal file
45
chrome/test/data/pdf/text_large.in
Normal file
@ -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
|
57
chrome/test/data/pdf/text_large.pdf
Normal file
57
chrome/test/data/pdf/text_large.pdf
Normal file
@ -0,0 +1,57 @@
|
||||
%PDF-1.7
|
||||
%<25><><EFBFBD><EFBFBD>
|
||||
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
|
@ -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())
|
||||
|
@ -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
|
||||
|
@ -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_) {
|
||||
|
@ -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;
|
||||
|
@ -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(
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
Reference in New Issue
Block a user