1 // Copyright 2020 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
6 #include "base/files/file_path.h"
7 #include "base/macros.h"
8 #include "base/run_loop.h"
9 #include "base/strings/utf_string_conversions.h"
10 #include "base/win/windows_version.h"
11 #include "build/build_config.h"
12 #include "chrome/browser/ui/browser.h"
13 #include "chrome/browser/ui/browser_window.h"
14 #include "chrome/browser/ui/tabs/tab_strip_model.h"
15 #include "chrome/test/base/in_process_browser_test.h"
16 #include "chrome/test/base/interactive_test_utils.h"
17 #include "chrome/test/base/ui_test_utils.h"
18 #include "content/public/browser/render_frame_host.h"
19 #include "content/public/browser/web_contents.h"
20 #include "content/public/common/content_switches.h"
21 #include "content/public/test/browser_test.h"
22 #include "content/public/test/browser_test_utils.h"
23 #include "content/public/test/text_input_test_utils.h"
24 #include "ui/base/ime/text_input_type.h"
25 #include "ui/base/test/ui_controls.h"
26 #include "ui/events/keycodes/keyboard_codes.h"
30 const int kTextAreaHeight = 36;
31 const int kTextAreaWidth = 162;
32 const int kTextAreaOffsetX = 100;
34 // TextInputManager Observers
36 // A base class for observing the TextInputManager owned by the given
37 // WebContents. Subclasses could observe the TextInputManager for different
38 // changes. The class wraps a public tester which accepts callbacks that
39 // are run after specific changes in TextInputManager. Different observers can
40 // be subclassed from this by providing their specific callback methods.
41 class TextInputManagerObserverBase {
43 explicit TextInputManagerObserverBase(content::WebContents* web_contents)
44 : tester_(new content::TextInputManagerTester(web_contents)),
47 virtual ~TextInputManagerObserverBase() {}
49 // Wait for derived class's definition of success.
56 bool success() const { return success_; }
59 content::TextInputManagerTester* tester() { return tester_.get(); }
65 // By deleting |tester_| we make sure that the internal observer used in
66 // content/ is removed from the observer list of TextInputManager.
67 tester_.reset(nullptr);
71 std::unique_ptr<content::TextInputManagerTester> tester_;
73 base::RunLoop run_loop;
75 DISALLOW_COPY_AND_ASSIGN(TextInputManagerObserverBase);
78 // This class observes TextInputManager for changes in
79 // |TextInputState.vk_policy|.
80 class TextInputManagerVkPolicyObserver : public TextInputManagerObserverBase {
82 TextInputManagerVkPolicyObserver(
83 content::WebContents* web_contents,
84 ui::mojom::VirtualKeyboardPolicy expected_value)
85 : TextInputManagerObserverBase(web_contents),
86 expected_value_(expected_value) {
87 tester()->SetUpdateTextInputStateCalledCallback(
88 base::BindRepeating(&TextInputManagerVkPolicyObserver::VerifyVkPolicy,
89 base::Unretained(this)));
93 void VerifyVkPolicy() {
94 ui::mojom::VirtualKeyboardPolicy value;
95 if (tester()->GetTextInputVkPolicy(&value) && expected_value_ == value)
99 ui::mojom::VirtualKeyboardPolicy expected_value_;
101 DISALLOW_COPY_AND_ASSIGN(TextInputManagerVkPolicyObserver);
104 // This class observes TextInputManager for changes in
105 // |TextInputState.last_vk_visibility_request|.
106 class TextInputManagerVkVisibilityRequestObserver
107 : public TextInputManagerObserverBase {
109 TextInputManagerVkVisibilityRequestObserver(
110 content::WebContents* web_contents,
111 ui::mojom::VirtualKeyboardVisibilityRequest expected_value)
112 : TextInputManagerObserverBase(web_contents),
113 expected_value_(expected_value) {
114 tester()->SetUpdateTextInputStateCalledCallback(base::BindRepeating(
115 &TextInputManagerVkVisibilityRequestObserver::VerifyVkVisibilityRequest,
116 base::Unretained(this)));
120 void VerifyVkVisibilityRequest() {
121 ui::mojom::VirtualKeyboardVisibilityRequest value;
122 if (tester()->GetTextInputVkVisibilityRequest(&value) &&
123 expected_value_ == value)
127 ui::mojom::VirtualKeyboardVisibilityRequest expected_value_;
129 DISALLOW_COPY_AND_ASSIGN(TextInputManagerVkVisibilityRequestObserver);
132 // This class observes TextInputManager for changes in
133 // |TextInputState.show_ime_if_needed|.
134 class TextInputManagerShowImeIfNeededObserver
135 : public TextInputManagerObserverBase {
137 TextInputManagerShowImeIfNeededObserver(content::WebContents* web_contents,
139 : TextInputManagerObserverBase(web_contents),
140 expected_value_(expected_value) {
141 tester()->SetUpdateTextInputStateCalledCallback(base::BindRepeating(
142 &TextInputManagerShowImeIfNeededObserver::VerifyShowImeIfNeeded,
143 base::Unretained(this)));
147 void VerifyShowImeIfNeeded() {
148 bool show_ime_if_needed;
149 if (tester()->GetTextInputShowImeIfNeeded(&show_ime_if_needed) &&
150 expected_value_ == show_ime_if_needed)
154 bool expected_value_;
156 DISALLOW_COPY_AND_ASSIGN(TextInputManagerShowImeIfNeededObserver);
159 class VirtualKeyboardPolicyTest : public InProcessBrowserTest {
161 VirtualKeyboardPolicyTest() {}
163 // InProcessBrowserTest:
164 void SetUpOnMainThread() override {
165 ASSERT_TRUE(ui_test_utils::BringBrowserWindowToFront(browser()));
168 content::WebContents* GetActiveWebContents() const {
169 return browser()->tab_strip_model()->GetActiveWebContents();
172 ui::BaseWindow* GetWindow() const { return browser()->window(); }
174 void SetUpCommandLine(base::CommandLine* command_line) override {
175 command_line->AppendSwitchASCII(switches::kEnableBlinkFeatures,
176 "VirtualKeyboard,EditContext");
177 InProcessBrowserTest::SetUpCommandLine(command_line);
180 // Wait for the active web contents title to match |title|.
181 void WaitForTitle(const std::string& title) {
182 const base::string16 expected_title(base::ASCIIToUTF16(title));
183 content::TitleWatcher title_watcher(GetActiveWebContents(), expected_title);
184 ASSERT_EQ(expected_title, title_watcher.WaitAndGetTitle());
187 void NavigateAndWaitForLoad() {
188 ASSERT_TRUE(ui_test_utils::BringBrowserWindowToFront(browser()));
190 // Navigate to the test page and wait for onload to be called.
191 const GURL url = ui_test_utils::GetTestUrl(
193 base::FilePath(FILE_PATH_LITERAL("virtual_keyboard_policy.html")));
194 ui_test_utils::NavigateToURL(browser(), url);
195 WaitForTitle("onload");
198 DISALLOW_COPY_AND_ASSIGN(VirtualKeyboardPolicyTest);
201 IN_PROC_BROWSER_TEST_F(VirtualKeyboardPolicyTest, Load) {
202 NavigateAndWaitForLoad();
205 // Tapping on an editable element should show VK.
206 IN_PROC_BROWSER_TEST_F(VirtualKeyboardPolicyTest, ShowVK) {
207 // ui_controls::SendTouchEvents which uses InjectTouchInput API only works
208 // on Windows 8 and up.
209 if (base::win::GetVersion() < base::win::Version::WIN8) {
213 NavigateAndWaitForLoad();
214 // Tap on the third textarea to open VK.
215 gfx::Rect bounds = GetActiveWebContents()->GetContainerBounds();
216 TextInputManagerVkPolicyObserver type_observer_auto(
217 GetActiveWebContents(), ui::mojom::VirtualKeyboardPolicy::AUTO);
218 ASSERT_TRUE(ui_controls::SendTouchEvents(
219 ui_controls::PRESS, 1,
220 bounds.x() + kTextAreaWidth / 2 + kTextAreaOffsetX * 2,
221 bounds.y() + kTextAreaHeight / 2));
222 WaitForTitle("focusin3");
223 type_observer_auto.Wait();
226 // Tapping on an editable element with virtualkeyboardpolicy="auto" should
227 // raise the VK but JS focus shouldn't.
228 IN_PROC_BROWSER_TEST_F(VirtualKeyboardPolicyTest, DontShowVKOnJSFocus) {
229 // ui_controls::SendTouchEvents which uses InjectTouchInput API only works
230 // on Windows 8 and up.
231 if (base::win::GetVersion() < base::win::Version::WIN8) {
235 NavigateAndWaitForLoad();
236 content::RenderFrameHost* current = GetActiveWebContents()->GetMainFrame();
237 TextInputManagerShowImeIfNeededObserver show_ime_observer_false(
238 GetActiveWebContents(), false);
239 // Run the JS that focuses the edit control.
241 "inputField = document.getElementById('txt4');"
242 "inputField.focus();";
243 EXPECT_TRUE(ExecuteScriptWithoutUserGesture(current, script));
244 show_ime_observer_false.Wait();
245 TextInputManagerShowImeIfNeededObserver show_ime_observer_true(
246 GetActiveWebContents(), true);
247 // Tap on the third textarea to open VK.
248 gfx::Rect bounds = GetActiveWebContents()->GetContainerBounds();
249 ASSERT_TRUE(ui_controls::SendTouchEvents(
250 ui_controls::PRESS, 1,
251 bounds.x() + kTextAreaWidth / 2 + kTextAreaOffsetX * 2,
252 bounds.y() + kTextAreaHeight / 2));
253 WaitForTitle("focusin3");
254 show_ime_observer_true.Wait();
257 // Tapping on an editable element with virtualkeyboardpolicy="manual" that
258 // calls hide() explicitly should hide VK.
259 IN_PROC_BROWSER_TEST_F(VirtualKeyboardPolicyTest, HideVK) {
260 // ui_controls::SendTouchEvents which uses InjectTouchInput API only works
261 // on Windows 8 and up.
262 if (base::win::GetVersion() < base::win::Version::WIN8) {
265 NavigateAndWaitForLoad();
267 // Tap on the first textarea that would trigger show() call.
268 gfx::Rect bounds = GetActiveWebContents()->GetContainerBounds();
269 TextInputManagerVkVisibilityRequestObserver type_observer_hide(
270 GetActiveWebContents(),
271 ui::mojom::VirtualKeyboardVisibilityRequest::HIDE);
272 ASSERT_TRUE(ui_controls::SendTouchEvents(
273 ui_controls::PRESS, 1, bounds.x() + kTextAreaWidth / 2 + kTextAreaOffsetX,
274 bounds.y() + kTextAreaHeight / 2));
275 WaitForTitle("focusin2");
276 type_observer_hide.Wait();
279 // Tapping on an editable element with virtualkeyboardpolicy="manual" that
280 // calls show() explicitly should show the VK and if hide()is called, then it
282 IN_PROC_BROWSER_TEST_F(VirtualKeyboardPolicyTest, ShowAndThenHideVK) {
283 // ui_controls::SendTouchEvents which uses InjectTouchInput API only works
284 // on Windows 8 and up.
285 if (base::win::GetVersion() < base::win::Version::WIN8) {
288 NavigateAndWaitForLoad();
290 // Tap on the first textarea that would trigger show() call and then on the
291 // second textarea that would trigger hide() call.
292 gfx::Rect bounds = GetActiveWebContents()->GetContainerBounds();
293 TextInputManagerVkVisibilityRequestObserver type_observer_show(
294 GetActiveWebContents(),
295 ui::mojom::VirtualKeyboardVisibilityRequest::SHOW);
296 ASSERT_TRUE(ui_controls::SendTouchEvents(ui_controls::PRESS, 1,
297 bounds.x() + kTextAreaWidth / 2,
298 bounds.y() + kTextAreaHeight / 2));
299 WaitForTitle("focusin1");
300 type_observer_show.Wait();
301 TextInputManagerVkVisibilityRequestObserver type_observer_hide(
302 GetActiveWebContents(),
303 ui::mojom::VirtualKeyboardVisibilityRequest::HIDE);
304 ASSERT_TRUE(ui_controls::SendTouchEvents(
305 ui_controls::PRESS, 1, bounds.x() + kTextAreaWidth / 2 + kTextAreaOffsetX,
306 bounds.y() + kTextAreaHeight / 2));
307 WaitForTitle("focusin2");
308 type_observer_hide.Wait();
311 // Tapping on an editable element with virtualkeyboardpolicy="manual" that
312 // calls show() explicitly should show the VK and if hide()is called, then it
313 // should hide VK on keydown.
314 IN_PROC_BROWSER_TEST_F(VirtualKeyboardPolicyTest, ShowAndThenHideVKOnKeyDown) {
315 // ui_controls::SendTouchEvents which uses InjectTouchInput API only works
316 // on Windows 8 and up.
317 if (base::win::GetVersion() < base::win::Version::WIN8) {
320 NavigateAndWaitForLoad();
322 // Tap on the first textarea that would trigger show() call and then on the
323 // second textarea that would trigger hide() call.
324 gfx::Rect bounds = GetActiveWebContents()->GetContainerBounds();
325 TextInputManagerVkVisibilityRequestObserver type_observer_show(
326 GetActiveWebContents(),
327 ui::mojom::VirtualKeyboardVisibilityRequest::SHOW);
328 ASSERT_TRUE(ui_controls::SendTouchEvents(ui_controls::PRESS, 1,
329 bounds.x() + kTextAreaWidth / 2,
330 bounds.y() + kTextAreaHeight / 2));
331 WaitForTitle("focusin1");
332 type_observer_show.Wait();
333 TextInputManagerVkVisibilityRequestObserver type_observer_hide(
334 GetActiveWebContents(),
335 ui::mojom::VirtualKeyboardVisibilityRequest::HIDE);
336 ASSERT_TRUE(ui_controls::SendKeyPress(GetWindow()->GetNativeWindow(),
337 ui::VKEY_RETURN, false, false, false,
339 WaitForTitle("KeyDown1");
340 type_observer_hide.Wait();
343 IN_PROC_BROWSER_TEST_F(VirtualKeyboardPolicyTest,
344 VKVisibilityRequestInDeletedDocument) {
345 // ui_controls::SendTouchEvents which uses InjectTouchInput API only works
346 // on Windows 8 and up.
347 if (base::win::GetVersion() < base::win::Version::WIN8) {
350 NavigateAndWaitForLoad();
352 // Tap on the first textarea that would trigger show() call and then on the
353 // second textarea that would trigger hide() call.
354 gfx::Rect bounds = GetActiveWebContents()->GetContainerBounds();
355 TextInputManagerVkVisibilityRequestObserver type_observer_none(
356 GetActiveWebContents(),
357 ui::mojom::VirtualKeyboardVisibilityRequest::NONE);
358 ASSERT_TRUE(ui_controls::SendTouchEvents(
359 ui_controls::PRESS, 1,
360 bounds.x() + kTextAreaWidth / 2 + kTextAreaOffsetX * 8,
361 bounds.y() + kTextAreaHeight / 2));
362 WaitForTitle("focusin5");
363 type_observer_none.Wait();
366 // Tapping on an editcontext with inputpanelpolicy="manual" that
367 // calls show() explicitly should show the VK and if hide() is called, then it
369 IN_PROC_BROWSER_TEST_F(VirtualKeyboardPolicyTest,
370 ShowAndThenHideVKInEditContext) {
371 // ui_controls::SendTouchEvents which uses InjectTouchInput API only works
372 // on Windows 8 and up.
373 if (base::win::GetVersion() < base::win::Version::WIN8) {
376 NavigateAndWaitForLoad();
378 // Tap on the textarea that would trigger show() call and then on the
379 // second textarea that would trigger hide() call.
380 gfx::Rect bounds = GetActiveWebContents()->GetContainerBounds();
381 TextInputManagerVkVisibilityRequestObserver type_observer_show(
382 GetActiveWebContents(),
383 ui::mojom::VirtualKeyboardVisibilityRequest::SHOW);
384 ASSERT_TRUE(ui_controls::SendTouchEvents(
385 ui_controls::PRESS, 1,
386 bounds.x() + kTextAreaWidth / 2 + kTextAreaOffsetX * 4,
387 bounds.y() + kTextAreaHeight / 2));
388 WaitForTitle("focusin4");
389 type_observer_show.Wait();
390 TextInputManagerVkVisibilityRequestObserver type_observer_hide(
391 GetActiveWebContents(),
392 ui::mojom::VirtualKeyboardVisibilityRequest::HIDE);
393 ASSERT_TRUE(ui_controls::SendKeyPress(GetWindow()->GetNativeWindow(),
394 ui::VKEY_RETURN, false, false, false,
396 WaitForTitle("hidevkin4");
397 type_observer_hide.Wait();