Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(125)

Side by Side Diff: chrome/browser/renderer_host/site_per_process_text_input_browsertest.cc

Issue 2354793003: Browser Side TextInputState Tracking for Android (Closed)
Patch Set: Rebased Created 4 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | chrome/test/BUILD.gn » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2016 The Chromium Authors. All rights reserved. 1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include <vector> 5 #include <vector>
6 6
7 #include "base/command_line.h" 7 #include "base/command_line.h"
8 #include "base/strings/utf_string_conversions.h" 8 #include "base/strings/utf_string_conversions.h"
9 #include "chrome/browser/chrome_content_browser_client.h" 9 #include "chrome/browser/chrome_content_browser_client.h"
10 #include "chrome/browser/ui/browser.h" 10 #include "chrome/browser/ui/browser.h"
(...skipping 19 matching lines...) Expand all
30 #include "ui/base/ime/text_input_client.h" 30 #include "ui/base/ime/text_input_client.h"
31 #include "ui/base/ime/text_input_mode.h" 31 #include "ui/base/ime/text_input_mode.h"
32 #include "ui/base/ime/text_input_type.h" 32 #include "ui/base/ime/text_input_type.h"
33 #include "url/gurl.h" 33 #include "url/gurl.h"
34 34
35 #if defined(OS_LINUX) && !defined(OS_CHROMEOS) 35 #if defined(OS_LINUX) && !defined(OS_CHROMEOS)
36 #include "ui/base/ime/linux/text_edit_command_auralinux.h" 36 #include "ui/base/ime/linux/text_edit_command_auralinux.h"
37 #include "ui/base/ime/linux/text_edit_key_bindings_delegate_auralinux.h" 37 #include "ui/base/ime/linux/text_edit_key_bindings_delegate_auralinux.h"
38 #endif 38 #endif
39 39
40 // TODO(ekaramad): The following tests should be active on all platforms. After
41 // fixing https://crbug.com/578168, this test file should be built for android
42 // as well and most of the following tests should be enabled for all platforms
43 //(https://crbug.com/602723).
44
45 /////////////////////////////////////////////////////////////////////////////// 40 ///////////////////////////////////////////////////////////////////////////////
46 // TextInputManager and IME Tests 41 // TextInputManager and IME Tests
47 // 42 //
48 // The following tests verify the correctness of TextInputState tracking on the 43 // The following tests verify the correctness of TextInputState tracking on the
49 // browser side. They also make sure the IME logic works correctly. The baseline 44 // browser side. They also make sure the IME logic works correctly. The baseline
50 // for comparison is the default functionality in the non-OOPIF case (i.e., the 45 // for comparison is the default functionality in the non-OOPIF case (i.e., the
51 // legacy implementation in RWHV's other than RWHVCF). 46 // legacy implementation in RWHV's other than RWHVCF).
52 // These tests live outside content/ because they rely on being part of the 47 // These tests live outside content/ because they rely on being part of the
53 // interactive UI test framework (to avoid flakiness). 48 // interactive UI test framework (to avoid flakiness).
54 49
(...skipping 272 matching lines...) Expand 10 before | Expand all | Expand 10 after
327 } 322 }
328 323
329 protected: 324 protected:
330 content::WebContents* active_contents() { 325 content::WebContents* active_contents() {
331 return browser()->tab_strip_model()->GetActiveWebContents(); 326 return browser()->tab_strip_model()->GetActiveWebContents();
332 } 327 }
333 328
334 // static 329 // static
335 // Adds an <input> field to a given frame by executing javascript code. 330 // Adds an <input> field to a given frame by executing javascript code.
336 // The input can be added as the first element or the last element of 331 // The input can be added as the first element or the last element of
337 // |document.body|. The text range defined by |selection_range| will be 332 // |document.body|.
338 // marked.
339 static void AddInputFieldToFrame(content::RenderFrameHost* rfh, 333 static void AddInputFieldToFrame(content::RenderFrameHost* rfh,
340 const std::string& type, 334 const std::string& type,
341 const std::string& value, 335 const std::string& value,
342 bool append_as_first_child) { 336 bool append_as_first_child) {
343 std::string script = base::StringPrintf( 337 std::string script = base::StringPrintf(
344 "var input = document.createElement('input');" 338 "var input = document.createElement('input');"
345 "input.setAttribute('type', '%s');" 339 "input.setAttribute('type', '%s');"
346 "input.setAttribute('value', '%s');" 340 "input.setAttribute('value', '%s');"
347 "document.body.%s;", 341 "document.body.%s;",
348 type.c_str(), value.c_str(), 342 type.c_str(), value.c_str(),
(...skipping 221 matching lines...) Expand 10 before | Expand all | Expand 10 after
570 SimulateKeyPress(active_contents(), ui::DomKey::TAB, ui::DomCode::TAB, 564 SimulateKeyPress(active_contents(), ui::DomKey::TAB, ui::DomCode::TAB,
571 ui::VKEY_TAB, false, false, false, false); 565 ui::VKEY_TAB, false, false, false, false);
572 set_state_observer.Wait(); 566 set_state_observer.Wait();
573 567
574 TextInputManagerTypeObserver reset_state_observer(active_contents(), 568 TextInputManagerTypeObserver reset_state_observer(active_contents(),
575 ui::TEXT_INPUT_TYPE_NONE); 569 ui::TEXT_INPUT_TYPE_NONE);
576 ui_test_utils::NavigateToURL(browser(), GURL("about:blank")); 570 ui_test_utils::NavigateToURL(browser(), GURL("about:blank"));
577 reset_state_observer.Wait(); 571 reset_state_observer.Wait();
578 } 572 }
579 573
574 // The following test verifies that when the active widget changes value, it is
575 // always from nullptr to non-null or vice versa.
576 IN_PROC_BROWSER_TEST_F(SitePerProcessTextInputManagerTest,
577 ResetTextInputStateOnActiveWidgetChange) {
578 CreateIframePage("a(b,c(a,b),d)");
579 std::vector<content::RenderFrameHost*> frames{
580 GetFrame(IndexVector{}), GetFrame(IndexVector{0}),
581 GetFrame(IndexVector{1}), GetFrame(IndexVector{1, 0}),
582 GetFrame(IndexVector{1, 1}), GetFrame(IndexVector{2})};
583 std::vector<content::RenderWidgetHostView*> views;
584 for (auto frame : frames)
585 views.push_back(frame->GetView());
586 std::vector<std::string> values{"a", "ab", "ac", "aca", "acb", "acd"};
587 for (size_t i = 0; i < frames.size(); ++i)
588 AddInputFieldToFrame(frames[i], "text", values[i], true);
589
590 content::WebContents* web_contents = active_contents();
591
592 auto send_tab_and_wait_for_value =
593 [&web_contents](const std::string& expected_value) {
594 TextInputManagerValueObserver observer(web_contents, expected_value);
595 SimulateKeyPress(web_contents, ui::DomKey::TAB, ui::DomCode::TAB,
596 ui::VKEY_TAB, false, false, false, false);
597 observer.Wait();
598 };
599
600 // Record all active view changes.
601 RecordActiveViewsObserver recorder(web_contents);
602 for (auto value : values)
603 send_tab_and_wait_for_value(value);
604
605 // We have covered a total of 6 views, so there should at least be 11 entries
606 // recorded (at least one null between two views).
607 size_t record_count = recorder.active_views()->size();
608 EXPECT_GT(record_count, 10U);
609
610 // Verify we do not have subsequent nullptr or non-nullptrs.
611 for (size_t i = 0; i < record_count - 1U; ++i) {
612 const content::RenderWidgetHostView* current =
613 recorder.active_views()->at(i);
614 const content::RenderWidgetHostView* next =
615 recorder.active_views()->at(i + 1U);
616 EXPECT_TRUE((current != nullptr && next == nullptr) ||
617 (current == nullptr && next != nullptr));
618 }
619 }
620
621 #if !defined(OS_ANDROID)
kenrb 2016/11/02 17:22:48 You removed the comment with the bug reference for
EhsanK 2016/11/02 20:45:52 I added it back to where !defined(OS_ANDROID) will
580 // This test creates a page with multiple child frames and adds an <input> to 622 // This test creates a page with multiple child frames and adds an <input> to
581 // each frame. Then, sequentially, each <input> is focused by sending a tab key. 623 // each frame. Then, sequentially, each <input> is focused by sending a tab key.
582 // Then, after |TextInputState.type| for a view is changed to text, the test 624 // Then, after |TextInputState.type| for a view is changed to text, the test
583 // sends a set composition IPC to the active widget and waits until the widget 625 // sends a set composition IPC to the active widget and waits until the widget
584 // updates its composition range. 626 // updates its composition range.
585 IN_PROC_BROWSER_TEST_F(SitePerProcessTextInputManagerTest, 627 IN_PROC_BROWSER_TEST_F(SitePerProcessTextInputManagerTest,
586 TrackCompositionRangeForAllFrames) { 628 TrackCompositionRangeForAllFrames) {
587 CreateIframePage("a(b,c(a,b),d)"); 629 CreateIframePage("a(b,c(a,b),d)");
588 std::vector<content::RenderFrameHost*> frames{ 630 std::vector<content::RenderFrameHost*> frames{
589 GetFrame(IndexVector{}), GetFrame(IndexVector{0}), 631 GetFrame(IndexVector{}), GetFrame(IndexVector{0}),
(...skipping 105 matching lines...) Expand 10 before | Expand all | Expand 10 after
695 for (size_t i = 0; i < views.size(); ++i) { 737 for (size_t i = 0; i < views.size(); ++i) {
696 // First focus the <input>. 738 // First focus the <input>.
697 send_tab_and_wait_for_value(values[i]); 739 send_tab_and_wait_for_value(values[i]);
698 740
699 // Send a sequence of |count| 'E' keys and wait until the view receives a 741 // Send a sequence of |count| 'E' keys and wait until the view receives a
700 // selection change update for a text of the corresponding size, |count|. 742 // selection change update for a text of the corresponding size, |count|.
701 send_keys_select_all_wait_for_selection_change(views[i], count++); 743 send_keys_select_all_wait_for_selection_change(views[i], count++);
702 } 744 }
703 } 745 }
704 746
705 // The following test verifies that when the active widget changes value, it is
706 // always from nullptr to non-null or vice versa.
707 IN_PROC_BROWSER_TEST_F(SitePerProcessTextInputManagerTest,
708 ResetTextInputStateOnActiveWidgetChange) {
709 CreateIframePage("a(b,c(a,b),d)");
710 std::vector<content::RenderFrameHost*> frames{
711 GetFrame(IndexVector{}), GetFrame(IndexVector{0}),
712 GetFrame(IndexVector{1}), GetFrame(IndexVector{1, 0}),
713 GetFrame(IndexVector{1, 1}), GetFrame(IndexVector{2})};
714 std::vector<content::RenderWidgetHostView*> views;
715 for (auto frame : frames)
716 views.push_back(frame->GetView());
717 std::vector<std::string> values{"a", "ab", "ac", "aca", "acb", "acd"};
718 for (size_t i = 0; i < frames.size(); ++i)
719 AddInputFieldToFrame(frames[i], "text", values[i], true);
720
721 content::WebContents* web_contents = active_contents();
722
723 auto send_tab_and_wait_for_value =
724 [&web_contents](const std::string& expected_value) {
725 TextInputManagerValueObserver observer(web_contents, expected_value);
726 SimulateKeyPress(web_contents, ui::DomKey::TAB, ui::DomCode::TAB,
727 ui::VKEY_TAB, false, false, false, false);
728 observer.Wait();
729 };
730
731 // Record all active view changes.
732 RecordActiveViewsObserver recorder(web_contents);
733 for (auto value : values)
734 send_tab_and_wait_for_value(value);
735
736 // We have covered a total of 6 views, so there should at least be 11 entries
737 // recorded (at least one null between two views).
738 size_t record_count = recorder.active_views()->size();
739 EXPECT_GT(record_count, 10U);
740
741 // Verify we do not have subsequent nullptr or non-nullptrs.
742 for (size_t i = 0; i < record_count - 1U; ++i) {
743 const content::RenderWidgetHostView* current =
744 recorder.active_views()->at(i);
745 const content::RenderWidgetHostView* next =
746 recorder.active_views()->at(i + 1U);
747 EXPECT_TRUE((current != nullptr && next == nullptr) ||
748 (current == nullptr && next != nullptr));
749 }
750 }
751
752 // TODO(ekaramad): The following tests are specifically written for Aura and are 747 // TODO(ekaramad): The following tests are specifically written for Aura and are
753 // based on InputMethodObserver. Write similar tests for Mac/Android/Mus 748 // based on InputMethodObserver. Write similar tests for Mac/Android/Mus
754 // (crbug.com/602723). 749 // (crbug.com/602723).
755 750
756 #if defined(USE_AURA) 751 #if defined(USE_AURA)
757 // ----------------------------------------------------------------------------- 752 // -----------------------------------------------------------------------------
758 // Input Method Observer Tests 753 // Input Method Observer Tests
759 // 754 //
760 // The following tests will make use of the InputMethodObserver to verify that 755 // The following tests will make use of the InputMethodObserver to verify that
761 // OOPIF pages interact properly with the InputMethod through the tab's view. 756 // OOPIF pages interact properly with the InputMethod through the tab's view.
(...skipping 184 matching lines...) Expand 10 before | Expand all | Expand 10 after
946 true, true, true, false)); 941 true, true, true, false));
947 ui::SetTextEditKeyBindingsDelegate(old_delegate); 942 ui::SetTextEditKeyBindingsDelegate(old_delegate);
948 943
949 // Verify that the input field in the subframe is erased. 944 // Verify that the input field in the subframe is erased.
950 EXPECT_TRUE(ExecuteScriptAndExtractString( 945 EXPECT_TRUE(ExecuteScriptAndExtractString(
951 child, "window.domAutomationController.send(getInputFieldText());", 946 child, "window.domAutomationController.send(getInputFieldText());",
952 &result)); 947 &result));
953 EXPECT_EQ("", result); 948 EXPECT_EQ("", result);
954 } 949 }
955 #endif 950 #endif
956 951 #endif // OS_ANDROID
957 // Ideally, the following code + test should be live in
958 // 'site_per_process_mac_browsertest.mm'. However, the test
959 // 'LookUpStringForRangeRoutesToFocusedWidget' relies on an override in
960 // ContentBrowserClient to register its filters in time. In content shell, we
961 // cannot have two instances of ShellContentBrowserClient (due to a DCHECK in
962 // the ctor). Therefore, we put the test here to use ChromeContentBrowserClient
963 // which does not have the same singleton constraint.
964 #if defined(OS_MACOSX)
965 // An observer of number of open windows.
966 class WindowCountObserver {
967 public:
968 explicit WindowCountObserver(size_t lower_limit) : limit_(lower_limit) {}
969 ~WindowCountObserver() {}
970
971 // Keep polling the count of open windows until the number exceeds |limit_|.
972 void WaitForLimitOrMore() {
973 size_t current_count = content::GetOpenNSWindowsCount();
974 if (current_count >= limit_)
975 return;
976
977 message_loop_runner_ = new content::MessageLoopRunner();
978 message_loop_runner_->Run();
979 content::BrowserThread::PostTask(
980 content::BrowserThread::UI, FROM_HERE,
981 base::Bind(&WindowCountObserver::CheckWindowCount,
982 base::Unretained(this)));
983 }
984
985 private:
986 void CheckWindowCount() {
987 size_t current_count = content::GetOpenNSWindowsCount();
988 if (current_count >= limit_) {
989 message_loop_runner_->Quit();
990 return;
991 }
992 content::BrowserThread::PostTask(
993 content::BrowserThread::UI, FROM_HERE,
994 base::Bind(&WindowCountObserver::CheckWindowCount,
995 base::Unretained(this)));
996 }
997
998 size_t limit_;
999 scoped_refptr<content::MessageLoopRunner> message_loop_runner_;
1000
1001 DISALLOW_COPY_AND_ASSIGN(WindowCountObserver);
1002 };
1003
1004 // The original TextInputClientMessageFilter is added during the initialization
1005 // phase of RenderProcessHost. The only chance we have to add the test filter
1006 // (so that it can receive the TextInputClientMac incoming IPC messages) is
1007 // during the call to RenderProcessWillLaunch() on ContentBrowserClient. This
1008 // class provides that for testing.
1009 class TestBrowserClient : public ChromeContentBrowserClient {
1010 public:
1011 TestBrowserClient() {}
1012 ~TestBrowserClient() override {}
1013
1014 // ContentBrowserClient overrides.
1015 void RenderProcessWillLaunch(
1016 content::RenderProcessHost* process_host) override {
1017 ChromeContentBrowserClient::RenderProcessWillLaunch(process_host);
1018 filters_.push_back(
1019 new content::TestTextInputClientMessageFilter(process_host));
1020 }
1021
1022 // Retrieves the registered filter for the given RenderProcessHost. It will
1023 // return false if the RenderProcessHost was initialized while a different
1024 // instance of ContentBrowserClient was in action.
1025 scoped_refptr<content::TestTextInputClientMessageFilter>
1026 GetTextInputClientMessageFilterForProcess(
1027 content::RenderProcessHost* process_host) const {
1028 for (auto filter : filters_) {
1029 if (filter->process() == process_host)
1030 return filter;
1031 }
1032 return nullptr;
1033 }
1034
1035 private:
1036 std::vector<scoped_refptr<content::TestTextInputClientMessageFilter>>
1037 filters_;
1038
1039 DISALLOW_COPY_AND_ASSIGN(TestBrowserClient);
1040 };
1041
1042 // This test verifies that requests for dictionary lookup based on selection
1043 // range are routed to the focused RenderWidgetHost.
1044 IN_PROC_BROWSER_TEST_F(SitePerProcessTextInputManagerTest,
1045 LookUpStringForRangeRoutesToFocusedWidget) {
kenrb 2016/11/02 17:22:48 Why is this test being removed?
EhsanK 2016/11/02 20:45:52 I lost this part in the rebase (the conflict was i
1046 // TestBrowserClient needs to replace the ChromeContenBrowserClient after most
1047 // things are initialized but before the WebContents is created. Here we make
1048 // that happen by creating a new WebContents in a new tab. But before the test
1049 // exits, we must destroy the contents and replace the old
1050 // ContentBrowserClient because the original WebContents and the new one have
1051 // been initialized with the original ContentBrowserClient and the new
1052 // TestBrowserClient, respectively.
1053 TestBrowserClient browser_client;
1054 content::ContentBrowserClient* old_browser_client =
1055 content::SetBrowserClientForTesting(&browser_client);
1056
1057 content::WebContents* new_contents =
1058 content::WebContents::Create(content::WebContents::CreateParams(
1059 active_contents()->GetBrowserContext(), nullptr));
1060 browser()->tab_strip_model()->InsertWebContentsAt(1, new_contents,
1061 TabStripModel::ADD_ACTIVE);
1062 EXPECT_EQ(active_contents(), new_contents);
1063
1064 // Starting the test body.
1065 CreateIframePage("a(b)");
1066 std::vector<content::RenderFrameHost*> frames{GetFrame(IndexVector{}),
1067 GetFrame(IndexVector{0})};
1068 std::vector<content::RenderWidgetHostView*> views;
1069 for (auto frame : frames)
1070 views.push_back(frame->GetView());
1071 std::vector<std::string> values{"main frame", "child frame"};
1072
1073 // Adding some field with text to each frame so that we can later query for
1074 // dictionary lookup.
1075 for (size_t i = 0; i < frames.size(); ++i)
1076 AddInputFieldToFrame(frames[i], "text", values[i], true);
1077
1078 std::string result;
1079 // Recording window count now so that our WindowCountObserver will detect the
1080 // popup window.
1081 size_t current_window_count = content::GetOpenNSWindowsCount();
1082
1083 // Ensure we have both focus and selected text inside the main frame.
1084 EXPECT_TRUE(
1085 ExecuteScript(frames[0], "document.querySelector('input').focus();"));
1086
1087 // Request for the dictionary lookup and intercept the word on its way back.
1088 // The request is always on the tab's view which is a RenderWidgetHostViewMac.
1089 content::AskForLookUpDictionaryForRange(views[0], gfx::Range(0, 4));
1090
1091 // Wait until the result comes back.
1092 auto root_filter = browser_client.GetTextInputClientMessageFilterForProcess(
1093 frames[0]->GetProcess());
1094 EXPECT_TRUE(root_filter);
1095 root_filter->WaitForStringFromRange();
1096
1097 EXPECT_EQ("main", root_filter->string_from_range());
1098
1099 // Wait for the popup to appear to make sure TextInputClientMac has consumed
1100 // the reply handler for the previous request.
1101 WindowCountObserver(current_window_count).WaitForLimitOrMore();
1102
1103 // Ensure we have both focus and selected text inside the child frame.
1104 EXPECT_TRUE(
1105 ExecuteScript(frames[1], "document.querySelector('input').focus();"));
1106
1107 // Record window count again for the popup observer.
1108 current_window_count = content::GetOpenNSWindowsCount();
1109
1110 // Make another request.
1111 content::AskForLookUpDictionaryForRange(views[0], gfx::Range(0, 5));
1112
1113 // Wait until the result comes back.
1114 auto child_filter = browser_client.GetTextInputClientMessageFilterForProcess(
1115 frames[1]->GetProcess());
1116 child_filter->WaitForStringFromRange();
1117
1118 EXPECT_EQ("child", child_filter->string_from_range());
1119
1120 // Wait for the popup to appear to make sure TextInputClientMac has consumed
1121 // the reply handler for the previous request.
1122 WindowCountObserver(current_window_count).WaitForLimitOrMore();
1123 // Test ends here. The rest is cleanup.
1124
1125 // Closing this WebContents while we still hold on to our TestBrowserClient.
1126 EXPECT_TRUE(browser()->tab_strip_model()->CloseWebContentsAt(
1127 1, TabStripModel::CLOSE_USER_GESTURE));
1128
1129 // For the cleanup of the original WebContents in tab index 0.
1130 content::SetBrowserClientForTesting(old_browser_client);
1131 }
1132 #endif
OLDNEW
« no previous file with comments | « no previous file | chrome/test/BUILD.gn » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698