Chromium Code Reviews| Index: chrome/browser/renderer_host/site_per_process_text_input_browsertest.cc |
| diff --git a/chrome/browser/renderer_host/site_per_process_text_input_browsertest.cc b/chrome/browser/renderer_host/site_per_process_text_input_browsertest.cc |
| index a06446b7ac41606c3d191efc851e72b01f240e52..0a46c318a08cfdd360aeacf576c4c5a917a64914 100644 |
| --- a/chrome/browser/renderer_host/site_per_process_text_input_browsertest.cc |
| +++ b/chrome/browser/renderer_host/site_per_process_text_input_browsertest.cc |
| @@ -24,11 +24,13 @@ |
| #include "content/public/test/text_input_test_utils.h" |
| #include "net/dns/mock_host_resolver.h" |
| #include "net/test/embedded_test_server/embedded_test_server.h" |
| +#include "ui/base/clipboard/clipboard.h" |
| #include "ui/base/ime/composition_underline.h" |
| #include "ui/base/ime/text_edit_commands.h" |
| #include "ui/base/ime/text_input_client.h" |
| #include "ui/base/ime/text_input_mode.h" |
| #include "ui/base/ime/text_input_type.h" |
| +#include "ui/base/test/ui_controls.h" |
| #include "url/gurl.h" |
| #if defined(OS_LINUX) && !defined(OS_CHROMEOS) |
| @@ -274,25 +276,44 @@ class ViewTextSelectionObserver : public TextInputManagerObserverBase { |
| class TextSelectionObserver : public TextInputManagerObserverBase { |
| public: |
| explicit TextSelectionObserver(content::WebContents* web_contents) |
| - : TextInputManagerObserverBase(web_contents) { |
| + : TextInputManagerObserverBase(web_contents), callback_called_(false) { |
| tester()->SetOnTextSelectionChangedCallback(base::Bind( |
| &TextSelectionObserver::VerifyChange, base::Unretained(this))); |
| } |
| - void WaitForSelectedText(const std::string& text) { |
| - selected_text_ = text; |
| - Wait(); |
| + void WaitForSelectedText(const std::string& text, |
| + bool user_initiated = true) { |
| + expected_text_ = text; |
| + expected_user_initiated_ = user_initiated; |
| + if (callback_called_ && last_selected_text_ == expected_text_ && |
| + last_user_initiated_ == expected_user_initiated_) { |
| + OnSuccess(); |
| + } else { |
| + Wait(); |
| + } |
| } |
| private: |
| void VerifyChange() { |
| - if (base::UTF16ToUTF8(tester()->GetUpdatedView()->GetSelectedText()) == |
| - selected_text_) { |
| + callback_called_ = true; |
| + |
| + last_selected_text_ = |
| + base::UTF16ToUTF8(tester()->GetUpdatedView()->GetSelectedText()); |
| + EXPECT_TRUE(tester()->GetTextSelectionUserInitiatedForView( |
| + tester()->GetUpdatedView(), &last_user_initiated_)); |
| + |
| + if (last_selected_text_ == expected_text_ && |
| + last_user_initiated_ == expected_user_initiated_) { |
| OnSuccess(); |
| } |
| } |
| - std::string selected_text_; |
| + bool callback_called_; |
| + |
| + std::string last_selected_text_; |
| + std::string expected_text_; |
| + bool last_user_initiated_; |
| + bool expected_user_initiated_; |
| DISALLOW_COPY_AND_ASSIGN(TextSelectionObserver); |
| }; |
| @@ -420,6 +441,58 @@ class SitePerProcessTextInputManagerTest : public InProcessBrowserTest { |
| return current; |
| } |
| + // Simulates a click on the middle of the first DOM element |
| + // with the given |selector| in the given frame |rfh|. The |frame_index| |
| + // parameter is used to find the child frame in the main frame and |
| + // use its offset to calculate the click position. |
| + void ClickFirstElementWithSelector(content::RenderFrameHost* rfh, |
| + int frame_index, |
| + const std::string& selector) { |
| + int frame_x = 0; |
| + int frame_y = 0; |
| + |
| + if (rfh != active_contents()->GetMainFrame()) { |
| + ASSERT_TRUE(content::ExecuteScriptAndExtractInt( |
| + active_contents()->GetMainFrame(), |
| + "var frames = document.querySelectorAll('iframe');" |
| + "var frame = frames[" + |
| + std::to_string(frame_index) + |
| + "];" |
| + "domAutomationController.send(Math.floor(frame.offsetLeft));", |
| + &frame_x)); |
| + ASSERT_TRUE(content::ExecuteScriptAndExtractInt( |
| + active_contents()->GetMainFrame(), |
| + "var frames = document.querySelectorAll('iframe');" |
| + "var frame = frames[" + |
| + std::to_string(frame_index) + |
| + "];" |
| + "domAutomationController.send(Math.floor(frame.offsetTop));", |
| + &frame_y)); |
| + } |
| + |
| + int x; |
| + ASSERT_TRUE(content::ExecuteScriptAndExtractInt( |
| + rfh, |
| + "var bounds = document.querySelector('" + selector + |
| + "').getBoundingClientRect();" |
| + "domAutomationController.send(" |
| + " Math.floor(bounds.left + bounds.width / 2));", |
| + &x)); |
| + int y; |
| + ASSERT_TRUE(content::ExecuteScriptAndExtractInt( |
| + rfh, |
| + "var bounds = document.querySelector('" + selector + |
| + "').getBoundingClientRect();" |
| + "domAutomationController.send(" |
| + " Math.floor(bounds.top + bounds.height / 2));", |
| + &y)); |
| + |
| + auto content_bounds = active_contents()->GetContainerBounds(); |
| + ui_controls::SendMouseMove(x + frame_x + content_bounds.x(), |
| + y + frame_y + content_bounds.y()); |
| + ui_controls::SendMouseClick(ui_controls::LEFT); |
| + } |
| + |
| private: |
| DISALLOW_COPY_AND_ASSIGN(SitePerProcessTextInputManagerTest); |
| }; |
| @@ -980,6 +1053,82 @@ IN_PROC_BROWSER_TEST_F(SitePerProcessTextInputManagerTest, |
| } |
| } |
| +// This test verifies that the TextInputManager notifies about a |
| +// text selection change event for both user and non-user (eg. JavaScript) |
| +// initiated cases. It is also tested whether TextSelection::user_initiated |
| +// is properly set. The content of the selection clipboard is also tested on |
| +// platforms where it is supported. |
| +// See: https://crbug.com/671986 |
| +IN_PROC_BROWSER_TEST_F(SitePerProcessTextInputManagerTest, |
| + NonUserInitiatedTextSelection) { |
| +#if defined(USE_AURA) && defined(OS_LINUX) && !defined(OS_CHROMEOS) |
| + ui::Clipboard* clipboard = ui::Clipboard::GetForCurrentThread(); |
| + EXPECT_TRUE(!!clipboard); |
|
Lei Zhang
2017/05/10 21:06:03
ASSERT_TRUE() because if this fails, the test exec
Peter Varga
2017/05/10 21:12:36
I copied this from another clipboard test. I will
|
| + clipboard->Clear(ui::CLIPBOARD_TYPE_SELECTION); |
| +#endif |
| + |
| + CreateIframePage("a(b, c)"); |
| + std::vector<std::string> values{"node_a", "node_b", "node_c"}; |
| + std::vector<content::RenderFrameHost*> frames{GetFrame(IndexVector{}), |
| + GetFrame(IndexVector{0}), |
| + GetFrame(IndexVector{1})}; |
| + |
| + for (size_t i = 0; i < frames.size(); ++i) |
| + AddInputFieldToFrame(frames[i], "text", values[i], true); |
| + |
| + // Test text selection from JavaScript across frames (non-user initiated). |
| + for (size_t i = 0; i < frames.size(); ++i) { |
| + // Trigger text selection. |
| + TextSelectionObserver trigger_observer(active_contents()); |
| + EXPECT_TRUE( |
| + ExecuteScript(frames[i], "document.querySelector('input').select();")); |
| + trigger_observer.WaitForSelectedText(values[i], false); |
| + |
| +#if defined(USE_AURA) && defined(OS_LINUX) && !defined(OS_CHROMEOS) |
| + // Non-user initiated text selection should not update the selection |
| + // clipboard. See: https://crbug.com/12392 |
| + base::string16 result_text; |
| + clipboard->ReadText(ui::CLIPBOARD_TYPE_SELECTION, &result_text); |
| + EXPECT_TRUE(result_text.empty()); |
| +#endif |
| + |
| + // Clear text selection. |
| + TextSelectionObserver clear_observer(active_contents()); |
| + EXPECT_TRUE(ExecuteScript(frames[i], "document.getSelection().empty();")); |
| + clear_observer.WaitForSelectedText("", false); |
| + } |
| + |
| + // Test text selection by user input across frames (user initiated). |
| + for (size_t i = 0; i < frames.size(); ++i) { |
| + // Click on element to have keyboard focus. |
| + TextInputManagerChangeObserver input_observer(active_contents()); |
| + // Index of the main frame is 0. Thus align child frame index to it (i-1). |
| + ASSERT_NO_FATAL_FAILURE( |
| + ClickFirstElementWithSelector(frames[i], i - 1, "input")); |
| + input_observer.Wait(); |
| + |
| + // Press ctrl+a to select text in the input field. |
| + TextSelectionObserver trigger_observer(active_contents()); |
| + SimulateKeyPress(active_contents(), ui::DomKey::FromCharacter('A'), |
| + ui::DomCode::US_A, ui::VKEY_A, true, false, false, false); |
| + trigger_observer.WaitForSelectedText(values[i], true); |
| + |
| +#if defined(USE_AURA) && defined(OS_LINUX) && !defined(OS_CHROMEOS) |
| + // User initiated text selection should update the selection clipboard. |
| + base::string16 result_text; |
| + clipboard->ReadText(ui::CLIPBOARD_TYPE_SELECTION, &result_text); |
| + EXPECT_EQ(base::ASCIIToUTF16(values[i]), result_text); |
| +#endif |
| + |
| + // Click on element to clear text selection. |
| + TextSelectionObserver clear_observer(active_contents()); |
| + // Index of the main frame is 0. Thus align child frame index to it (i-1). |
| + ASSERT_NO_FATAL_FAILURE( |
| + ClickFirstElementWithSelector(frames[i], i - 1, "input")); |
| + clear_observer.WaitForSelectedText("", true); |
| + } |
| +} |
| + |
| // TODO(ekaramad): The following tests are specifically written for Aura and are |
| // based on InputMethodObserver. Write similar tests for Mac/Android/Mus |
| // (crbug.com/602723). |