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
3 // found in the LICENSE file.
4 
5 #include <vector>
6 
7 #include "base/bind.h"
8 #include "base/command_line.h"
9 #include "base/run_loop.h"
10 #include "base/strings/stringprintf.h"
11 #include "base/strings/utf_string_conversions.h"
12 #include "build/build_config.h"
13 #include "chrome/browser/ui/browser.h"
14 #include "chrome/browser/ui/browser_window.h"
15 #include "chrome/browser/ui/location_bar/location_bar.h"
16 #include "chrome/browser/ui/tabs/tab_strip_model.h"
17 #include "chrome/test/base/in_process_browser_test.h"
18 #include "chrome/test/base/interactive_test_utils.h"
19 #include "chrome/test/base/ui_test_utils.h"
20 #include "content/public/browser/browser_task_traits.h"
21 #include "content/public/browser/browser_thread.h"
22 #include "content/public/browser/content_browser_client.h"
23 #include "content/public/browser/render_frame_host.h"
24 #include "content/public/browser/render_process_host.h"
25 #include "content/public/browser/render_widget_host.h"
26 #include "content/public/browser/render_widget_host_view.h"
27 #include "content/public/browser/web_contents.h"
28 #include "content/public/common/content_client.h"
29 #include "content/public/test/browser_test.h"
30 #include "content/public/test/browser_test_utils.h"
31 #include "content/public/test/content_browser_test_utils.h"
32 #include "content/public/test/test_utils.h"
33 #include "content/public/test/text_input_test_utils.h"
34 #include "net/dns/mock_host_resolver.h"
35 #include "net/test/embedded_test_server/embedded_test_server.h"
36 #include "ui/base/ime/ime_text_span.h"
37 #include "ui/base/ime/text_edit_commands.h"
38 #include "ui/base/ime/text_input_client.h"
39 #include "ui/base/ime/text_input_mode.h"
40 #include "ui/base/ime/text_input_type.h"
41 #include "url/gurl.h"
42 
43 #if defined(OS_LINUX) && !defined(OS_CHROMEOS)
44 #include "ui/base/ime/linux/text_edit_command_auralinux.h"
45 #include "ui/base/ime/linux/text_edit_key_bindings_delegate_auralinux.h"
46 #endif
47 
48 ///////////////////////////////////////////////////////////////////////////////
49 // TextInputManager and IME Tests
50 //
51 // The following tests verify the correctness of TextInputState tracking on the
52 // browser side. They also make sure the IME logic works correctly. The baseline
53 // for comparison is the default functionality in the non-OOPIF case (i.e., the
54 // legacy implementation in RWHV's other than RWHVCF).
55 // These tests live outside content/ because they rely on being part of the
56 // interactive UI test framework (to avoid flakiness).
57 
58 namespace {
59 using IndexVector = std::vector<size_t>;
60 
61 // This class observes TextInputManager for the first change in TextInputState.
62 class TextInputManagerChangeObserver
63     : public content::TextInputManagerObserverBase {
64  public:
TextInputManagerChangeObserver(content::WebContents * web_contents)65   explicit TextInputManagerChangeObserver(content::WebContents* web_contents)
66       : TextInputManagerObserverBase(web_contents) {
67     tester()->SetUpdateTextInputStateCalledCallback(base::Bind(
68         &TextInputManagerChangeObserver::VerifyChange, base::Unretained(this)));
69   }
70 
71  private:
VerifyChange()72   void VerifyChange() {
73     if (tester()->IsTextInputStateChanged())
74       OnSuccess();
75   }
76 
77   DISALLOW_COPY_AND_ASSIGN(TextInputManagerChangeObserver);
78 };
79 
80 // This class observes |TextInputState.type| for a specific RWHV.
81 class ViewTextInputTypeObserver : public content::TextInputManagerObserverBase {
82  public:
ViewTextInputTypeObserver(content::WebContents * web_contents,content::RenderWidgetHostView * rwhv,ui::TextInputType expected_type)83   explicit ViewTextInputTypeObserver(content::WebContents* web_contents,
84                                      content::RenderWidgetHostView* rwhv,
85                                      ui::TextInputType expected_type)
86       : TextInputManagerObserverBase(web_contents),
87         web_contents_(web_contents),
88         view_(rwhv),
89         expected_type_(expected_type) {
90     tester()->SetUpdateTextInputStateCalledCallback(base::Bind(
91         &ViewTextInputTypeObserver::VerifyType, base::Unretained(this)));
92   }
93 
94  private:
VerifyType()95   void VerifyType() {
96     ui::TextInputType type;
97     if (!content::GetTextInputTypeForView(web_contents_, view_, &type))
98       return;
99     if (expected_type_ == type)
100       OnSuccess();
101   }
102 
103   content::WebContents* web_contents_;
104   content::RenderWidgetHostView* view_;
105   const ui::TextInputType expected_type_;
106 
107   DISALLOW_COPY_AND_ASSIGN(ViewTextInputTypeObserver);
108 };
109 
110 // This class observes the |expected_view| for the first change in its
111 // selection bounds.
112 class ViewSelectionBoundsChangedObserver
113     : public content::TextInputManagerObserverBase {
114  public:
ViewSelectionBoundsChangedObserver(content::WebContents * web_contents,content::RenderWidgetHostView * expected_view)115   ViewSelectionBoundsChangedObserver(
116       content::WebContents* web_contents,
117       content::RenderWidgetHostView* expected_view)
118       : TextInputManagerObserverBase(web_contents),
119         expected_view_(expected_view) {
120     tester()->SetOnSelectionBoundsChangedCallback(
121         base::Bind(&ViewSelectionBoundsChangedObserver::VerifyChange,
122                    base::Unretained(this)));
123   }
124 
125  private:
VerifyChange()126   void VerifyChange() {
127     if (expected_view_ == tester()->GetUpdatedView())
128       OnSuccess();
129   }
130 
131   const content::RenderWidgetHostView* const expected_view_;
132 
133   DISALLOW_COPY_AND_ASSIGN(ViewSelectionBoundsChangedObserver);
134 };
135 
136 // This class observes the |expected_view| for the first change in its
137 // composition range information.
138 class ViewCompositionRangeChangedObserver
139     : public content::TextInputManagerObserverBase {
140  public:
ViewCompositionRangeChangedObserver(content::WebContents * web_contents,content::RenderWidgetHostView * expected_view)141   ViewCompositionRangeChangedObserver(
142       content::WebContents* web_contents,
143       content::RenderWidgetHostView* expected_view)
144       : TextInputManagerObserverBase(web_contents),
145         expected_view_(expected_view) {
146     tester()->SetOnImeCompositionRangeChangedCallback(
147         base::Bind(&ViewCompositionRangeChangedObserver::VerifyChange,
148                    base::Unretained(this)));
149   }
150 
151  private:
VerifyChange()152   void VerifyChange() {
153     if (expected_view_ == tester()->GetUpdatedView())
154       OnSuccess();
155   }
156 
157   const content::RenderWidgetHostView* const expected_view_;
158 
159   DISALLOW_COPY_AND_ASSIGN(ViewCompositionRangeChangedObserver);
160 };
161 
162 // This class observes the |expected_view| for a change in the text selection.
163 class ViewTextSelectionObserver : public content::TextInputManagerObserverBase {
164  public:
ViewTextSelectionObserver(content::WebContents * web_contents,content::RenderWidgetHostView * expected_view,size_t expected_length)165   ViewTextSelectionObserver(content::WebContents* web_contents,
166                             content::RenderWidgetHostView* expected_view,
167                             size_t expected_length)
168       : TextInputManagerObserverBase(web_contents),
169         expected_view_(expected_view),
170         expected_length_(expected_length) {
171     tester()->SetOnTextSelectionChangedCallback(base::Bind(
172         &ViewTextSelectionObserver::VerifyChange, base::Unretained(this)));
173   }
174 
175  private:
VerifyChange()176   void VerifyChange() {
177     if (expected_view_ == tester()->GetUpdatedView()) {
178       size_t length;
179       EXPECT_TRUE(tester()->GetCurrentTextSelectionLength(&length));
180       if (length == expected_length_)
181         OnSuccess();
182     }
183   }
184 
185   const content::RenderWidgetHostView* const expected_view_;
186   const size_t expected_length_;
187 
188   DISALLOW_COPY_AND_ASSIGN(ViewTextSelectionObserver);
189 };
190 
191 // This class observes all the text selection updates within a WebContents.
192 class TextSelectionObserver : public content::TextInputManagerObserverBase {
193  public:
TextSelectionObserver(content::WebContents * web_contents)194   explicit TextSelectionObserver(content::WebContents* web_contents)
195       : TextInputManagerObserverBase(web_contents) {
196     tester()->SetOnTextSelectionChangedCallback(base::Bind(
197         &TextSelectionObserver::VerifyChange, base::Unretained(this)));
198   }
199 
WaitForSelectedText(const std::string & text)200   void WaitForSelectedText(const std::string& text) {
201     selected_text_ = text;
202     Wait();
203   }
204 
205  private:
VerifyChange()206   void VerifyChange() {
207     if (base::UTF16ToUTF8(tester()->GetUpdatedView()->GetSelectedText()) ==
208         selected_text_) {
209       OnSuccess();
210     }
211   }
212 
213   std::string selected_text_;
214 
215   DISALLOW_COPY_AND_ASSIGN(TextSelectionObserver);
216 };
217 
218 // This class monitors all the changes in TextInputState and keeps a record of
219 // the active views. There is no waiting and the recording process is
220 // continuous.
221 class RecordActiveViewsObserver {
222  public:
RecordActiveViewsObserver(content::WebContents * web_contents)223   explicit RecordActiveViewsObserver(content::WebContents* web_contents)
224       : tester_(new content::TextInputManagerTester(web_contents)) {
225     tester_->SetUpdateTextInputStateCalledCallback(base::Bind(
226         &RecordActiveViewsObserver::RecordActiveView, base::Unretained(this)));
227   }
228 
active_views() const229   const std::vector<const content::RenderWidgetHostView*>* active_views()
230       const {
231     return &active_views_;
232   }
233 
234  private:
RecordActiveView()235   void RecordActiveView() {
236     if (!tester_->IsTextInputStateChanged())
237       return;
238     active_views_.push_back(tester_->GetActiveView());
239   }
240 
241   std::unique_ptr<content::TextInputManagerTester> tester_;
242   std::vector<const content::RenderWidgetHostView*> active_views_;
243 
244   DISALLOW_COPY_AND_ASSIGN(RecordActiveViewsObserver);
245 };
246 
247 }  // namespace
248 
249 // Main class for all TextInputState and IME related tests.
250 class SitePerProcessTextInputManagerTest : public InProcessBrowserTest {
251  public:
SitePerProcessTextInputManagerTest()252   SitePerProcessTextInputManagerTest() {}
~SitePerProcessTextInputManagerTest()253   ~SitePerProcessTextInputManagerTest() override {}
254 
SetUpCommandLine(base::CommandLine * command_line)255   void SetUpCommandLine(base::CommandLine* command_line) override {
256     content::IsolateAllSitesForTesting(command_line);
257   }
258 
SetUpOnMainThread()259   void SetUpOnMainThread() override {
260     host_resolver()->AddRule("*", "127.0.0.1");
261 
262     // Add content/test/data 'cross_site_iframe_factory.html'.
263     embedded_test_server()->ServeFilesFromSourceDirectory("content/test/data");
264 
265     ASSERT_TRUE(embedded_test_server()->Start());
266   }
267 
268  protected:
active_contents()269   content::WebContents* active_contents() {
270     return browser()->tab_strip_model()->GetActiveWebContents();
271   }
272 
273   // static
274   // Adds an <input> field to a given frame by executing javascript code.
275   // The input can be added as the first element or the last element of
276   // |document.body|. The text range defined by |selection_range| will be
277   // marked.
AddInputFieldToFrame(content::RenderFrameHost * rfh,const std::string & type,const std::string & value,bool append_as_first_child)278   static void AddInputFieldToFrame(content::RenderFrameHost* rfh,
279                                    const std::string& type,
280                                    const std::string& value,
281                                    bool append_as_first_child) {
282     std::string script = base::StringPrintf(
283         "var input = document.createElement('input');"
284         "input.setAttribute('type', '%s');"
285         "input.setAttribute('value', '%s');"
286         "document.body.%s;",
287         type.c_str(), value.c_str(),
288         append_as_first_child ? "insertBefore(input, document.body.firstChild)"
289                               : "appendChild(input)");
290     EXPECT_TRUE(ExecuteScript(rfh, script));
291   }
292 
293   // static
294   // Appends an <input> field with various attribues to a given frame by
295   // executing javascript code.
AppendInputFieldToFrame(content::RenderFrameHost * rfh,const std::string & type,const std::string & id,const std::string & value,const std::string & placeholder)296   static void AppendInputFieldToFrame(content::RenderFrameHost* rfh,
297                                       const std::string& type,
298                                       const std::string& id,
299                                       const std::string& value,
300                                       const std::string& placeholder) {
301     std::string script = base::StringPrintf(
302         "var input = document.createElement('input');"
303         "input.setAttribute('type', '%s');"
304         "input.setAttribute('id', '%s');"
305         "input.setAttribute('value', '%s');"
306         "input.setAttribute('placeholder', '%s');"
307         "document.body.appendChild(input);",
308         type.c_str(), id.c_str(), value.c_str(), placeholder.c_str());
309     EXPECT_TRUE(ExecuteScript(rfh, script));
310   }
311 
312   // static
313   // Focus a form field by its Id.
FocusFormField(content::RenderFrameHost * rfh,const std::string & id)314   static void FocusFormField(content::RenderFrameHost* rfh,
315                              const std::string& id) {
316     std::string script = base::StringPrintf(
317         "document.getElementById('%s').focus();", id.c_str());
318 
319     EXPECT_TRUE(ExecuteScript(rfh, script));
320   }
321 
322   // Uses 'cross_site_iframe_factory.html'. The main frame's domain is
323   // 'a.com'.
CreateIframePage(const std::string & structure)324   void CreateIframePage(const std::string& structure) {
325     std::string path = base::StringPrintf("/cross_site_iframe_factory.html?%s",
326                                           structure.c_str());
327     GURL main_url(embedded_test_server()->GetURL("a.com", path));
328     ui_test_utils::NavigateToURL(browser(), main_url);
329   }
330 
331   // Iteratively uses ChildFrameAt(frame, i) to get the i-th child frame
332   // inside frame. For example, for 'a(b(c, d(e)))', [0] returns b, and
333   // [0, 1, 0] returns e;
GetFrame(const IndexVector & indices)334   content::RenderFrameHost* GetFrame(const IndexVector& indices) {
335     content::RenderFrameHost* current = active_contents()->GetMainFrame();
336     for (size_t index : indices)
337       current = ChildFrameAt(current, index);
338     return current;
339   }
340 
341  private:
342   DISALLOW_COPY_AND_ASSIGN(SitePerProcessTextInputManagerTest);
343 };
344 
345 // The following test loads a page with multiple nested <iframe> elements which
346 // are in or out of process with the main frame. Then an <input> field with
347 // unique value is added to every single frame on the frame tree. The test then
348 // creates a sequence of tab presses and verifies that after each key press, the
349 // TextInputState.value reflects that of the focused input, i.e., the
350 // TextInputManager is correctly tracking TextInputState across frames.
351 // Flaky on ChromeOS, Linux, Mac, and Windows; https://crbug.com/704994.
IN_PROC_BROWSER_TEST_F(SitePerProcessTextInputManagerTest,DISABLED_TrackStateWhenSwitchingFocusedFrames)352 IN_PROC_BROWSER_TEST_F(SitePerProcessTextInputManagerTest,
353                        DISABLED_TrackStateWhenSwitchingFocusedFrames) {
354   CreateIframePage("a(a,b,c(a,b,d(e, f)),g)");
355   std::vector<std::string> values{
356       "main",     "node_a",   "node_b",     "node_c",     "node_c_a",
357       "node_c_b", "node_c_d", "node_c_d_e", "node_c_d_f", "node_g"};
358 
359   // TODO(ekaramad): The use for explicitly constructing the IndexVector from
360   // initializer list should not be necessary. However, some chromeos bots throw
361   //  errors if we do not do it like this.
362   std::vector<content::RenderFrameHost*> frames{
363       GetFrame(IndexVector{}),        GetFrame(IndexVector{0}),
364       GetFrame(IndexVector{1}),       GetFrame(IndexVector{2}),
365       GetFrame(IndexVector{2, 0}),    GetFrame(IndexVector{2, 1}),
366       GetFrame(IndexVector{2, 2}),    GetFrame(IndexVector{2, 2, 0}),
367       GetFrame(IndexVector{2, 2, 1}), GetFrame(IndexVector{3})};
368 
369   for (size_t i = 0; i < frames.size(); ++i)
370     AddInputFieldToFrame(frames[i], "text", values[i], true);
371 
372   for (size_t i = 0; i < frames.size(); ++i) {
373     content::TextInputManagerValueObserver observer(active_contents(),
374                                                     values[i]);
375     SimulateKeyPress(active_contents(), ui::DomKey::TAB, ui::DomCode::TAB,
376                      ui::VKEY_TAB, false, false, false, false);
377     observer.Wait();
378   }
379 }
380 
381 // The following test loads a page with two OOPIFs. An <input> is added to both
382 // frames and tab key is pressed until the one in the second OOPIF is focused.
383 // Then, the renderer processes for both frames are crashed. The test verifies
384 // that the TextInputManager stops tracking the RWHVs as well as properly
385 // resets the TextInputState after the second (active) RWHV goes away.
IN_PROC_BROWSER_TEST_F(SitePerProcessTextInputManagerTest,StopTrackingCrashedChildFrame)386 IN_PROC_BROWSER_TEST_F(SitePerProcessTextInputManagerTest,
387                        StopTrackingCrashedChildFrame) {
388   CreateIframePage("a(b, c)");
389   std::vector<std::string> values{"node_b", "node_c"};
390   std::vector<content::RenderFrameHost*> frames{GetFrame(IndexVector{0}),
391                                                 GetFrame(IndexVector{1})};
392 
393   for (size_t i = 0; i < frames.size(); ++i)
394     AddInputFieldToFrame(frames[i], "text", values[i], true);
395 
396   // Tab into both inputs and make sure we correctly receive their
397   // TextInputState. For the second tab two IPCs arrive: one from the first
398   // frame to set the state to none, and another one from the second frame to
399   // set it to TEXT. To avoid the race between them, we shall also observe the
400   // first frame setting its state to NONE after the second tab.
401   ViewTextInputTypeObserver view_type_observer(
402       active_contents(), frames[0]->GetView(), ui::TEXT_INPUT_TYPE_NONE);
403 
404   for (size_t i = 0; i < frames.size(); ++i) {
405     content::TextInputManagerValueObserver observer(active_contents(),
406                                                     values[i]);
407     SimulateKeyPress(active_contents(), ui::DomKey::TAB, ui::DomCode::TAB,
408                      ui::VKEY_TAB, false, false, false, false);
409     observer.Wait();
410   }
411 
412   // Make sure that the first view has set its TextInputState.type to NONE.
413   view_type_observer.Wait();
414 
415   // Verify that we are tracking the TextInputState from the first frame.
416   content::RenderWidgetHostView* first_view = frames[0]->GetView();
417   ui::TextInputType first_view_type;
418   EXPECT_TRUE(content::GetTextInputTypeForView(active_contents(), first_view,
419                                                &first_view_type));
420   EXPECT_EQ(ui::TEXT_INPUT_TYPE_NONE, first_view_type);
421 
422   size_t registered_views_count =
423       content::GetRegisteredViewsCountFromTextInputManager(active_contents());
424 
425   // We expect at least two views for the two child frames.
426   EXPECT_GT(registered_views_count, 2U);
427 
428   // Now that the second frame's <input> is focused, we crash the first frame
429   // and observe that text input state is updated for the view.
430   std::unique_ptr<content::TestRenderWidgetHostViewDestructionObserver>
431       destruction_observer(
432           new content::TestRenderWidgetHostViewDestructionObserver(first_view));
433   {
434     content::ScopedAllowRendererCrashes allow_renderer_crashes(frames[0]);
435     frames[0]->GetProcess()->Shutdown(0);
436     destruction_observer->Wait();
437   }
438 
439   // Verify that the TextInputManager is no longer tracking TextInputState for
440   // |first_view|.
441   EXPECT_EQ(
442       registered_views_count - 1U,
443       content::GetRegisteredViewsCountFromTextInputManager(active_contents()));
444 
445   // Now crash the second <iframe> which has an active view.
446   destruction_observer.reset(
447       new content::TestRenderWidgetHostViewDestructionObserver(
448           frames[1]->GetView()));
449   {
450     content::ScopedAllowRendererCrashes allow_renderer_crashes(frames[1]);
451     frames[1]->GetProcess()->Shutdown(0);
452     destruction_observer->Wait();
453   }
454 
455   EXPECT_EQ(
456       registered_views_count - 2U,
457       content::GetRegisteredViewsCountFromTextInputManager(active_contents()));
458 }
459 
460 // The following test loads a page with two child frames: one in process and one
461 // out of process with main frame. The test inserts an <input> inside each frame
462 // and focuses the first frame and observes the TextInputManager setting the
463 // state to ui::TEXT_INPUT_TYPE_TEXT. Then, the frame is detached and the test
464 // observes that the state type is reset to ui::TEXT_INPUT_TYPE_NONE. The same
465 // sequence of actions is then performed on the out of process frame.
IN_PROC_BROWSER_TEST_F(SitePerProcessTextInputManagerTest,ResetStateAfterFrameDetached)466 IN_PROC_BROWSER_TEST_F(SitePerProcessTextInputManagerTest,
467                        ResetStateAfterFrameDetached) {
468   CreateIframePage("a(a, b)");
469   std::vector<content::RenderFrameHost*> frames{GetFrame(IndexVector{0}),
470                                                 GetFrame(IndexVector{1})};
471 
472   for (size_t i = 0; i < frames.size(); ++i)
473     AddInputFieldToFrame(frames[i], "text", "", true);
474 
475   // Press tab key to focus the <input> in the first frame.
476   content::TextInputManagerTypeObserver type_observer_text_a(
477       active_contents(), ui::TEXT_INPUT_TYPE_TEXT);
478   SimulateKeyPress(active_contents(), ui::DomKey::TAB, ui::DomCode::TAB,
479                    ui::VKEY_TAB, false, false, false, false);
480   type_observer_text_a.Wait();
481 
482   std::string remove_first_iframe_script =
483       "var frame = document.querySelector('iframe');"
484       "frame.parentNode.removeChild(frame);";
485   // Detach first frame and observe |TextInputState.type| resetting to
486   // ui::TEXT_INPUT_TYPE_NONE.
487   content::TextInputManagerTypeObserver type_observer_none_a(
488       active_contents(), ui::TEXT_INPUT_TYPE_NONE);
489   EXPECT_TRUE(ExecuteScript(active_contents(), remove_first_iframe_script));
490   type_observer_none_a.Wait();
491 
492   // Press tab to focus the <input> in the second frame.
493   content::TextInputManagerTypeObserver type_observer_text_b(
494       active_contents(), ui::TEXT_INPUT_TYPE_TEXT);
495   SimulateKeyPress(active_contents(), ui::DomKey::TAB, ui::DomCode::TAB,
496                    ui::VKEY_TAB, false, false, false, false);
497   type_observer_text_b.Wait();
498 
499   // Detach first frame and observe |TextInputState.type| resetting to
500   // ui::TEXT_INPUT_TYPE_NONE.
501   content::TextInputManagerTypeObserver type_observer_none_b(
502       active_contents(), ui::TEXT_INPUT_TYPE_NONE);
503   EXPECT_TRUE(ExecuteScript(active_contents(), remove_first_iframe_script));
504   type_observer_none_b.Wait();
505 }
506 
507 // This test creates a page with one OOPIF and adds an <input> to it. Then, the
508 // <input> is focused and the test verfies that the |TextInputState.type| is set
509 // to ui::TEXT_INPUT_TYPE_TEXT. Next, the child frame is navigated away and the
510 // test verifies that |TextInputState.type| resets to ui::TEXT_INPUT_TYPE_NONE.
IN_PROC_BROWSER_TEST_F(SitePerProcessTextInputManagerTest,ResetStateAfterChildNavigation)511 IN_PROC_BROWSER_TEST_F(SitePerProcessTextInputManagerTest,
512                        ResetStateAfterChildNavigation) {
513   CreateIframePage("a(b)");
514   content::RenderFrameHost* main_frame = GetFrame(IndexVector{});
515   content::RenderFrameHost* child_frame = GetFrame(IndexVector{0});
516 
517   AddInputFieldToFrame(child_frame, "text", "child", false);
518 
519   // Focus <input> in child frame and verify the |TextInputState.value|.
520   content::TextInputManagerValueObserver child_set_state_observer(
521       active_contents(), "child");
522   SimulateKeyPress(active_contents(), ui::DomKey::TAB, ui::DomCode::TAB,
523                    ui::VKEY_TAB, false, false, false, false);
524   child_set_state_observer.Wait();
525 
526   // Navigate the child frame to about:blank and verify that TextInputManager
527   // correctly sets its |TextInputState.type| to ui::TEXT_INPUT_TYPE_NONE.
528   content::TextInputManagerTypeObserver child_reset_state_observer(
529       active_contents(), ui::TEXT_INPUT_TYPE_NONE);
530   EXPECT_TRUE(ExecuteScript(
531       main_frame, "document.querySelector('iframe').src = 'about:blank'"));
532   child_reset_state_observer.Wait();
533 }
534 
535 // This test creates a blank page and adds an <input> to it. Then, the <input>
536 // is focused and the test verfies that the |TextInputState.type| is set to
537 // ui::TEXT_INPUT_TYPE_TEXT. Next, the browser is navigated away and the test
538 // verifies that |TextInputState.type| resets to ui::TEXT_INPUT_TYPE_NONE.
IN_PROC_BROWSER_TEST_F(SitePerProcessTextInputManagerTest,ResetStateAfterBrowserNavigation)539 IN_PROC_BROWSER_TEST_F(SitePerProcessTextInputManagerTest,
540                        ResetStateAfterBrowserNavigation) {
541   CreateIframePage("a()");
542   content::RenderFrameHost* main_frame = GetFrame(IndexVector{});
543   AddInputFieldToFrame(main_frame, "text", "", false);
544 
545   content::TextInputManagerTypeObserver set_state_observer(
546       active_contents(), ui::TEXT_INPUT_TYPE_TEXT);
547   SimulateKeyPress(active_contents(), ui::DomKey::TAB, ui::DomCode::TAB,
548                    ui::VKEY_TAB, false, false, false, false);
549   set_state_observer.Wait();
550 
551   content::TextInputManagerTypeObserver reset_state_observer(
552       active_contents(), ui::TEXT_INPUT_TYPE_NONE);
553   ui_test_utils::NavigateToURL(browser(), GURL("about:blank"));
554   reset_state_observer.Wait();
555 }
556 
557 #if defined(USE_AURA)
558 // This test creates a blank page and adds an <input> to it. Then, the <input>
559 // is focused, UI is focused, then the input is refocused. The test verifies
560 // that selection bounds change with the refocus (see https://crbug.com/864563).
IN_PROC_BROWSER_TEST_F(SitePerProcessTextInputManagerTest,SelectionBoundsChangeAfterRefocusInput)561 IN_PROC_BROWSER_TEST_F(SitePerProcessTextInputManagerTest,
562                        SelectionBoundsChangeAfterRefocusInput) {
563   CreateIframePage("a()");
564   content::RenderFrameHost* main_frame = GetFrame(IndexVector{});
565   content::RenderWidgetHostView* view = main_frame->GetView();
566   content::WebContents* web_contents = active_contents();
567   AddInputFieldToFrame(main_frame, "text", "", false);
568 
569   auto focus_input_and_wait_for_selection_bounds_change =
570       [&main_frame, &web_contents, &view]() {
571         ViewSelectionBoundsChangedObserver bounds_observer(web_contents, view);
572         // SimulateKeyPress(web_contents, ui::DomKey::TAB, ui::DomCode::TAB,
573         //               ui::VKEY_TAB, false, true, false, false);
574         EXPECT_TRUE(ExecuteScript(main_frame,
575                                   "document.querySelector('input').focus();"));
576         bounds_observer.Wait();
577       };
578 
579   focus_input_and_wait_for_selection_bounds_change();
580 
581   // Focus location bar.
582   BrowserWindow* window = browser()->window();
583   ASSERT_TRUE(window);
584   LocationBar* location_bar = window->GetLocationBar();
585   ASSERT_TRUE(location_bar);
586   location_bar->FocusLocation(true);
587 
588   focus_input_and_wait_for_selection_bounds_change();
589 }
590 #endif
591 
592 // This test verifies that if we have a focused <input> in the main frame and
593 // the tab is closed, TextInputManager handles unregistering itself and
594 // notifying the observers properly (see https://crbug.com/669375).
IN_PROC_BROWSER_TEST_F(SitePerProcessTextInputManagerTest,ClosingTabWillNotCrash)595 IN_PROC_BROWSER_TEST_F(SitePerProcessTextInputManagerTest,
596                        ClosingTabWillNotCrash) {
597   CreateIframePage("a()");
598   content::RenderFrameHost* main_frame = GetFrame(IndexVector{});
599   AddInputFieldToFrame(main_frame, "text", "", false);
600 
601   // Focus the input and wait for state update.
602   content::TextInputManagerTypeObserver observer(active_contents(),
603                                                  ui::TEXT_INPUT_TYPE_TEXT);
604   SimulateKeyPress(active_contents(), ui::DomKey::TAB, ui::DomCode::TAB,
605                    ui::VKEY_TAB, false, false, false, false);
606   observer.Wait();
607 
608   // Now destroy the tab. We should exit without crashing.
609   browser()->tab_strip_model()->CloseWebContentsAt(
610       0, TabStripModel::CLOSE_USER_GESTURE);
611 }
612 
613 // The following test verifies that when the active widget changes value, it is
614 // always from nullptr to non-null or vice versa.
IN_PROC_BROWSER_TEST_F(SitePerProcessTextInputManagerTest,ResetTextInputStateOnActiveWidgetChange)615 IN_PROC_BROWSER_TEST_F(SitePerProcessTextInputManagerTest,
616                        ResetTextInputStateOnActiveWidgetChange) {
617   CreateIframePage("a(b,c(a,b),d)");
618   std::vector<content::RenderFrameHost*> frames{
619       GetFrame(IndexVector{}),     GetFrame(IndexVector{0}),
620       GetFrame(IndexVector{1}),    GetFrame(IndexVector{1, 0}),
621       GetFrame(IndexVector{1, 1}), GetFrame(IndexVector{2})};
622   std::vector<content::RenderWidgetHostView*> views;
623   for (auto* frame : frames)
624     views.push_back(frame->GetView());
625   std::vector<std::string> values{"a", "ab", "ac", "aca", "acb", "acd"};
626   for (size_t i = 0; i < frames.size(); ++i)
627     AddInputFieldToFrame(frames[i], "text", values[i], true);
628 
629   content::WebContents* web_contents = active_contents();
630 
631   auto send_tab_and_wait_for_value =
632       [&web_contents](const std::string& expected_value) {
633         content::TextInputManagerValueObserver observer(web_contents,
634                                                         expected_value);
635         SimulateKeyPress(web_contents, ui::DomKey::TAB, ui::DomCode::TAB,
636                          ui::VKEY_TAB, false, false, false, false);
637         observer.Wait();
638       };
639 
640   // Record all active view changes.
641   RecordActiveViewsObserver recorder(web_contents);
642   for (auto value : values)
643     send_tab_and_wait_for_value(value);
644 
645   // We have covered a total of 6 views, so there should at least be 11 entries
646   // recorded (at least one null between two views).
647   size_t record_count = recorder.active_views()->size();
648   EXPECT_GT(record_count, 10U);
649 
650   // Verify we do not have subsequent nullptr or non-nullptrs.
651   for (size_t i = 0; i < record_count - 1U; ++i) {
652     const content::RenderWidgetHostView* current =
653         recorder.active_views()->at(i);
654     const content::RenderWidgetHostView* next =
655         recorder.active_views()->at(i + 1U);
656     EXPECT_TRUE((current != nullptr && next == nullptr) ||
657                 (current == nullptr && next != nullptr));
658   }
659 }
660 
661 // This test creates a page with multiple child frames and adds an <input> to
662 // each frame. Then, sequentially, each <input> is focused by sending a tab key.
663 // Then, after |TextInputState.type| for a view is changed to text, the test
664 // sends a set composition IPC to the active widget and waits until the widget
665 // updates its composition range.
IN_PROC_BROWSER_TEST_F(SitePerProcessTextInputManagerTest,TrackCompositionRangeForAllFrames)666 IN_PROC_BROWSER_TEST_F(SitePerProcessTextInputManagerTest,
667                        TrackCompositionRangeForAllFrames) {
668   CreateIframePage("a(b,c(a,b),d)");
669   std::vector<content::RenderFrameHost*> frames{
670       GetFrame(IndexVector{}),     GetFrame(IndexVector{0}),
671       GetFrame(IndexVector{1}),    GetFrame(IndexVector{1, 0}),
672       GetFrame(IndexVector{1, 1}), GetFrame(IndexVector{2})};
673   std::vector<content::RenderWidgetHostView*> views;
674   for (auto* frame : frames)
675     views.push_back(frame->GetView());
676   for (size_t i = 0; i < frames.size(); ++i)
677     AddInputFieldToFrame(frames[i], "text", "text", true);
678 
679   content::WebContents* web_contents = active_contents();
680 
681   auto send_tab_set_composition_wait_for_bounds_change =
682       [&web_contents](content::RenderWidgetHostView* view) {
683         ViewTextInputTypeObserver type_observer(web_contents, view,
684                                                 ui::TEXT_INPUT_TYPE_TEXT);
685         SimulateKeyPress(web_contents, ui::DomKey::TAB, ui::DomCode::TAB,
686                          ui::VKEY_TAB, false, false, false, false);
687         type_observer.Wait();
688 
689         ViewCompositionRangeChangedObserver range_observer(web_contents, view);
690         EXPECT_TRUE(
691             content::RequestCompositionInfoFromActiveWidget(web_contents));
692         range_observer.Wait();
693       };
694 
695   for (auto* view : views)
696     send_tab_set_composition_wait_for_bounds_change(view);
697 }
698 
699 // Failing on Mac - http://crbug.com/852452
700 #if defined(OS_MAC)
701 #define MAYBE_TrackTextSelectionForAllFrames \
702   DISABLED_TrackTextSelectionForAllFrames
703 #else
704 #define MAYBE_TrackTextSelectionForAllFrames TrackTextSelectionForAllFrames
705 #endif
706 
707 // This test creates a page with multiple child frames and adds an <input> to
708 // each frame. Then, sequentially, each <input> is focused by sending a tab key.
709 // After focusing each input, a sequence of key presses (character 'E') are sent
710 // to the focused widget. The test then verifies that the selection length
711 // equals the length of the sequence of 'E's.
IN_PROC_BROWSER_TEST_F(SitePerProcessTextInputManagerTest,MAYBE_TrackTextSelectionForAllFrames)712 IN_PROC_BROWSER_TEST_F(SitePerProcessTextInputManagerTest,
713                        MAYBE_TrackTextSelectionForAllFrames) {
714   CreateIframePage("a(b,c(a,b),d)");
715   std::vector<content::RenderFrameHost*> frames{
716       GetFrame(IndexVector{}),     GetFrame(IndexVector{0}),
717       GetFrame(IndexVector{1}),    GetFrame(IndexVector{1, 0}),
718       GetFrame(IndexVector{1, 1}), GetFrame(IndexVector{2})};
719   std::vector<std::string> values{"main", "b", "c", "ca", "cb", "d"};
720   std::vector<content::RenderWidgetHostView*> views;
721   for (auto* frame : frames)
722     views.push_back(frame->GetView());
723   for (size_t i = 0; i < frames.size(); ++i)
724     AddInputFieldToFrame(frames[i], "text", values[i], true);
725 
726   content::WebContents* web_contents = active_contents();
727 
728   auto send_tab_and_wait_for_value = [&web_contents](const std::string& value) {
729     content::TextInputManagerValueObserver observer(web_contents, value);
730     SimulateKeyPress(web_contents, ui::DomKey::TAB, ui::DomCode::TAB,
731                      ui::VKEY_TAB, false, false, false, false);
732     observer.Wait();
733   };
734 
735   auto send_keys_select_all_wait_for_selection_change =
736       [&web_contents](content::RenderWidgetHostView* view, size_t count) {
737         ViewTextSelectionObserver observer(web_contents, view, count);
738         for (size_t i = 0; i < count; ++i) {
739           SimulateKeyPress(web_contents, ui::DomKey::FromCharacter('E'),
740                            ui::DomCode::US_E, ui::VKEY_E, false, false, false,
741                            false);
742         }
743         observer.Wait();
744       };
745 
746   size_t count = 2;
747   for (size_t i = 0; i < views.size(); ++i) {
748     // First focus the <input>.
749     send_tab_and_wait_for_value(values[i]);
750 
751     // Send a sequence of |count| 'E' keys and wait until the view receives a
752     // selection change update for a text of the corresponding size, |count|.
753     send_keys_select_all_wait_for_selection_change(views[i], count++);
754   }
755 }
756 
757 // This test verifies that committing text works as expected for all the frames
758 // on the page. Specifically, the test sends an IPC to the RenderWidget
759 // corresponding to a focused frame with a focused <input> to commit some text.
760 // Then, it verifies that the <input>'s value matches the committed text
761 // (https://crbug.com/688842).
762 // Flaky on Android and Linux http://crbug.com/852274
763 #if defined(OS_MAC)
764 #define MAYBE_ImeCommitTextForAllFrames DISABLED_ImeCommitTextForAllFrames
765 #else
766 #define MAYBE_ImeCommitTextForAllFrames ImeCommitTextForAllFrames
767 #endif
IN_PROC_BROWSER_TEST_F(SitePerProcessTextInputManagerTest,MAYBE_ImeCommitTextForAllFrames)768 IN_PROC_BROWSER_TEST_F(SitePerProcessTextInputManagerTest,
769                        MAYBE_ImeCommitTextForAllFrames) {
770   CreateIframePage("a(b,c(a))");
771   std::vector<content::RenderFrameHost*> frames{
772       GetFrame(IndexVector{}), GetFrame(IndexVector{0}),
773       GetFrame(IndexVector{1}), GetFrame(IndexVector{1, 0})};
774   for (size_t i = 0; i < frames.size(); ++i)
775     AddInputFieldToFrame(frames[i], "text", "", true);
776 
777   std::vector<std::string> sample_text{"main", "child_b", "child_c", "child_a"};
778   ASSERT_EQ(frames.size(), sample_text.size());
779 
780   // An observer of all text selection updates within a WebContents.
781   TextSelectionObserver observer(active_contents());
782   for (size_t index = 0; index < frames.size(); ++index) {
783     // Focus the input and listen to 'input' event inside the frame. When the
784     // event fires, select all the text inside the input. This will trigger a
785     // selection update on the browser side.
786     ASSERT_TRUE(ExecuteScript(frames[index],
787                               "window.focus();"
788                               "var input = document.querySelector('input');"
789                               "input.focus();"
790                               "window.addEventListener('input', function(e) {"
791                               "  input.select();"
792                               "});"))
793         << "Could not run script in frame with index:" << index;
794 
795     // Commit some text for this frame.
796     content::SendImeCommitTextToWidget(
797         frames[index]->GetView()->GetRenderWidgetHost(),
798         base::UTF8ToUTF16(sample_text[index]), std::vector<ui::ImeTextSpan>(),
799         gfx::Range(), 0);
800 
801     // Verify that the text we committed is now selected by listening to a
802     // selection update from a RenderWidgetHostView which has the expected
803     // selected text.
804     observer.WaitForSelectedText(sample_text[index]);
805   }
806 }
807 
808 // TODO(ekaramad): Some of the following tests should be active on Android as
809 // well. Enable them when the corresponding feature is implemented for Android
810 // (https://crbug.com/602723).
811 #if !defined(OS_ANDROID)
812 // This test creates a page with multiple child frames and adds an <input> to
813 // each frame. Then, sequentially, each <input> is focused by sending a tab key.
814 // Then, after |TextInputState.type| for a view is changed to text, another key
815 // is pressed (a character) and then the test verifies that TextInputManager
816 // receives the corresponding update on the change in selection bounds on the
817 // browser side.
IN_PROC_BROWSER_TEST_F(SitePerProcessTextInputManagerTest,TrackSelectionBoundsForAllFrames)818 IN_PROC_BROWSER_TEST_F(SitePerProcessTextInputManagerTest,
819                        TrackSelectionBoundsForAllFrames) {
820   CreateIframePage("a(b,c(a,b),d)");
821   std::vector<content::RenderFrameHost*> frames{
822       GetFrame(IndexVector{}),     GetFrame(IndexVector{0}),
823       GetFrame(IndexVector{1}),    GetFrame(IndexVector{1, 0}),
824       GetFrame(IndexVector{1, 1}), GetFrame(IndexVector{2})};
825   std::vector<content::RenderWidgetHostView*> views;
826   for (auto* frame : frames)
827     views.push_back(frame->GetView());
828   for (size_t i = 0; i < frames.size(); ++i)
829     AddInputFieldToFrame(frames[i], "text", "", true);
830 
831   content::WebContents* web_contents = active_contents();
832 
833   auto send_tab_insert_text_wait_for_bounds_change =
834       [&web_contents](content::RenderWidgetHostView* view) {
835         ViewTextInputTypeObserver type_observer(web_contents, view,
836                                                 ui::TEXT_INPUT_TYPE_TEXT);
837         SimulateKeyPress(web_contents, ui::DomKey::TAB, ui::DomCode::TAB,
838                          ui::VKEY_TAB, false, false, false, false);
839         type_observer.Wait();
840         ViewSelectionBoundsChangedObserver bounds_observer(web_contents, view);
841         SimulateKeyPress(web_contents, ui::DomKey::FromCharacter('E'),
842                          ui::DomCode::US_E, ui::VKEY_E, false, false, false,
843                          false);
844         bounds_observer.Wait();
845       };
846 
847   for (auto* view : views)
848     send_tab_insert_text_wait_for_bounds_change(view);
849 }
850 
851 // This test makes sure browser correctly tracks focused editable element inside
852 // each RenderFrameHost.
853 // Test is flaky on chromeOS; https://crbug.com/705203.
854 #if defined(OS_CHROMEOS)
855 #define MAYBE_TrackingFocusedElementForAllFrames \
856   DISABLED_TrackingFocusedElementForAllFrames
857 #else
858 #define MAYBE_TrackingFocusedElementForAllFrames \
859   TrackingFocusedElementForAllFrames
860 #endif
IN_PROC_BROWSER_TEST_F(SitePerProcessTextInputManagerTest,MAYBE_TrackingFocusedElementForAllFrames)861 IN_PROC_BROWSER_TEST_F(SitePerProcessTextInputManagerTest,
862                        MAYBE_TrackingFocusedElementForAllFrames) {
863   CreateIframePage("a(a, b(a))");
864   std::vector<content::RenderFrameHost*> frames{
865       GetFrame(IndexVector{}), GetFrame(IndexVector{0}),
866       GetFrame(IndexVector{1}), GetFrame(IndexVector{1, 0})};
867   for (size_t i = 0; i < frames.size(); ++i)
868     AddInputFieldToFrame(frames[i], "text", "some text", true);
869 
870   // Focus the <input> in |frame| and return if RenderFrameHost thinks there is
871   // a focused editable element in it.
872   auto focus_input_and_return_editable_element_state =
873       [](content::RenderFrameHost* frame) {
874         EXPECT_TRUE(
875             ExecuteScript(frame, "document.querySelector('input').focus();"));
876         return content::DoesFrameHaveFocusedEditableElement(frame);
877       };
878 
879   // When focusing an <input> we should receive an update.
880   for (auto* frame : frames)
881     EXPECT_TRUE(focus_input_and_return_editable_element_state(frame));
882 
883   // Blur the <input> in |frame| and return if RenderFrameHost thinks there is a
884   // focused editable element in it.
885   auto blur_input_and_return_editable_element_state =
886       [](content::RenderFrameHost* frame) {
887         EXPECT_TRUE(
888             ExecuteScript(frame, "document.querySelector('input').blur();"));
889         return content::DoesFrameHaveFocusedEditableElement(frame);
890       };
891 
892   // Similarly, we should receive updates when losing focus.
893   for (auto* frame : frames)
894     EXPECT_FALSE(blur_input_and_return_editable_element_state(frame));
895 }
896 
897 // This test tracks page level focused editable element tracking using
898 // WebContents. In a page with multiple frames, a frame is selected and
899 // focused. Then the <input> inside frame is both focused and blurred and  and
900 // in both cases the test verifies that WebContents is aware whether or not a
901 // focused editable element exists on the page.
902 // Test is flaky on ChromeOS. crbug.com/705289
903 #if defined(OS_CHROMEOS)
904 #define MAYBE_TrackPageFocusEditableElement \
905   DISABLED_TrackPageFocusEditableElement
906 #else
907 #define MAYBE_TrackPageFocusEditableElement TrackPageFocusEditableElement
908 #endif
IN_PROC_BROWSER_TEST_F(SitePerProcessTextInputManagerTest,MAYBE_TrackPageFocusEditableElement)909 IN_PROC_BROWSER_TEST_F(SitePerProcessTextInputManagerTest,
910                        MAYBE_TrackPageFocusEditableElement) {
911   CreateIframePage("a(a, b(a))");
912   std::vector<content::RenderFrameHost*> frames{
913       GetFrame(IndexVector{}), GetFrame(IndexVector{0}),
914       GetFrame(IndexVector{1}), GetFrame(IndexVector{1, 0})};
915   for (size_t i = 0; i < frames.size(); ++i)
916     AddInputFieldToFrame(frames[i], "text", "some text", true);
917 
918   auto focus_frame = [](content::RenderFrameHost* frame) {
919     EXPECT_TRUE(ExecuteScript(frame, "window.focus();"));
920   };
921 
922   auto set_input_focus = [](content::RenderFrameHost* frame, bool focus) {
923     EXPECT_TRUE(ExecuteScript(
924         frame, base::StringPrintf("document.querySelector('input').%s();",
925                                   (focus ? "focus" : "blur"))));
926   };
927 
928   for (auto* frame : frames) {
929     focus_frame(frame);
930     // Focus the <input>.
931     set_input_focus(frame, true);
932     EXPECT_TRUE(active_contents()->IsFocusedElementEditable());
933     // No blur <input>.
934     set_input_focus(frame, false);
935     EXPECT_FALSE(active_contents()->IsFocusedElementEditable());
936   }
937 }
938 
939 // TODO(ekaramad): Could this become a unit test instead?
940 // This test focuses <input> elements on the page and verifies that
941 // WebContents knows about the focused editable element. Then it asks the
942 // WebContents to clear focused element and verifies that there is no longer
943 // a focused editable element on the page.
944 // Test is flaky on ChromeOS; https://crbug.com/705203.
945 #if defined(OS_CHROMEOS)
946 #define MAYBE_ClearFocusedElementOnPage DISABLED_ClearFocusedElementOnPage
947 #else
948 #define MAYBE_ClearFocusedElementOnPage ClearFocusedElementOnPage
949 #endif
IN_PROC_BROWSER_TEST_F(SitePerProcessTextInputManagerTest,MAYBE_ClearFocusedElementOnPage)950 IN_PROC_BROWSER_TEST_F(SitePerProcessTextInputManagerTest,
951                        MAYBE_ClearFocusedElementOnPage) {
952   CreateIframePage("a(a, b(a))");
953   std::vector<content::RenderFrameHost*> frames{
954       GetFrame(IndexVector{}), GetFrame(IndexVector{0}),
955       GetFrame(IndexVector{1}), GetFrame(IndexVector{1, 0})};
956   for (size_t i = 0; i < frames.size(); ++i)
957     AddInputFieldToFrame(frames[i], "text", "some text", true);
958 
959   auto focus_frame_and_input = [](content::RenderFrameHost* frame) {
960     EXPECT_TRUE(ExecuteScript(frame,
961                               "window.focus();"
962                               "document.querySelector('input').focus();"));
963   };
964 
965   for (auto* frame : frames) {
966     focus_frame_and_input(frame);
967     EXPECT_TRUE(active_contents()->IsFocusedElementEditable());
968     active_contents()->ClearFocusedElement();
969     EXPECT_FALSE(active_contents()->IsFocusedElementEditable());
970   }
971 }
972 
973 // TODO(ekaramad): The following tests are specifically written for Aura and are
974 // based on InputMethodObserver. Write similar tests for Mac/Android/Mus
975 // (crbug.com/602723).
976 #if defined(USE_AURA)
977 // -----------------------------------------------------------------------------
978 // Input Method Observer Tests
979 //
980 // The following tests will make use of the InputMethodObserver to verify that
981 // OOPIF pages interact properly with the InputMethod through the tab's view.
982 
983 // TODO(ekaramad): We only have coverage for some aura tests as the whole idea
984 // of ui::TextInputClient/ui::InputMethod/ui::InputMethodObserver seems to be
985 // only fit to aura (specifically, OS_CHROMEOS). Can we add more tests here for
986 // aura as well as other platforms (https://crbug.com/602723)?
987 
988 // Observes current input method for state changes.
989 class InputMethodObserverBase {
990  public:
InputMethodObserverBase(content::WebContents * web_contents)991   explicit InputMethodObserverBase(content::WebContents* web_contents)
992       : success_(false),
993         test_observer_(content::TestInputMethodObserver::Create(web_contents)) {
994   }
995 
Wait()996   void Wait() {
997     if (success_)
998       return;
999     message_loop_runner_ = new content::MessageLoopRunner();
1000     message_loop_runner_->Run();
1001   }
1002 
success() const1003   bool success() const { return success_; }
1004 
1005  protected:
test_observer()1006   content::TestInputMethodObserver* test_observer() {
1007     return test_observer_.get();
1008   }
1009 
success_closure()1010   const base::Closure success_closure() {
1011     return base::Bind(&InputMethodObserverBase::OnSuccess,
1012                       base::Unretained(this));
1013   }
1014 
1015  private:
OnSuccess()1016   void OnSuccess() {
1017     success_ = true;
1018     if (message_loop_runner_)
1019       message_loop_runner_->Quit();
1020   }
1021 
1022   bool success_;
1023   std::unique_ptr<content::TestInputMethodObserver> test_observer_;
1024   scoped_refptr<content::MessageLoopRunner> message_loop_runner_;
1025 
1026   DISALLOW_COPY_AND_ASSIGN(InputMethodObserverBase);
1027 };
1028 
1029 class InputMethodObserverForShowIme : public InputMethodObserverBase {
1030  public:
InputMethodObserverForShowIme(content::WebContents * web_contents)1031   explicit InputMethodObserverForShowIme(content::WebContents* web_contents)
1032       : InputMethodObserverBase(web_contents) {
1033     test_observer()->SetOnShowVirtualKeyboardIfEnabledCallback(
1034         success_closure());
1035   }
1036 
1037  private:
1038   DISALLOW_COPY_AND_ASSIGN(InputMethodObserverForShowIme);
1039 };
1040 
1041 // TODO(ekaramad): This test is actually a unit test and should be moved to
1042 // somewhere more relevant (https://crbug.com/602723).
1043 // This test verifies that the IME for Aura is shown if and only if the current
1044 // client's |TextInputState.type| is not ui::TEXT_INPUT_TYPE_NONE and the flag
1045 // |TextInputState.show_ime_if_needed| is true. This should happen even when
1046 // the TextInputState has not changed (according to the platform), e.g., in
1047 // aura when receiving two consecutive updates with same |TextInputState.type|.
1048 
1049 // This test is disabled on Windows because we have removed TryShow/TryHide API
1050 // calls and replaced it with TSF input pane policy which is a policy applied by
1051 // text service framework on Windows based on whether TSF edit control has focus
1052 // or not. On Windows we have implemented TSF1 on Chromium that takes care of
1053 // IME compositions, handwriting panels, SIP visibility etc. Please see
1054 // (https://crbug.com/1007958) for more details.
1055 #if !defined(OS_WIN)
IN_PROC_BROWSER_TEST_F(SitePerProcessTextInputManagerTest,CorrectlyShowVirtualKeyboardIfEnabled)1056 IN_PROC_BROWSER_TEST_F(SitePerProcessTextInputManagerTest,
1057                        CorrectlyShowVirtualKeyboardIfEnabled) {
1058   // We only need the <iframe> page to create RWHV.
1059   CreateIframePage("a()");
1060   content::RenderFrameHost* main_frame = GetFrame(IndexVector{});
1061   content::RenderWidgetHostView* view = main_frame->GetView();
1062   content::WebContents* web_contents = active_contents();
1063 
1064   content::TextInputStateSender sender(view);
1065 
1066   auto send_and_check_show_ime = [&sender, &web_contents]() {
1067     InputMethodObserverForShowIme observer(web_contents);
1068     sender.Send();
1069     return observer.success();
1070   };
1071 
1072   // Sending an empty state should not trigger ime.
1073   EXPECT_FALSE(send_and_check_show_ime());
1074 
1075   // Set |TextInputState.type| to text. Expect no IME.
1076   sender.SetType(ui::TEXT_INPUT_TYPE_TEXT);
1077   EXPECT_FALSE(send_and_check_show_ime());
1078 
1079   // Set |TextInputState.show_ime_if_needed| to true. Expect IME.
1080   sender.SetShowVirtualKeyboardIfEnabled(true);
1081   EXPECT_TRUE(send_and_check_show_ime());
1082 
1083   // Send the same message. Expect IME (no change).
1084   EXPECT_TRUE(send_and_check_show_ime());
1085 
1086   // Reset |TextInputState.show_ime_if_needed|. Expect no IME.
1087   sender.SetShowVirtualKeyboardIfEnabled(false);
1088   EXPECT_FALSE(send_and_check_show_ime());
1089 
1090   // Setting an irrelevant field. Expect no IME.
1091   sender.SetMode(ui::TEXT_INPUT_MODE_TEXT);
1092   EXPECT_FALSE(send_and_check_show_ime());
1093 
1094   // Set |TextInputState.show_ime_if_needed|. Expect IME.
1095   sender.SetShowVirtualKeyboardIfEnabled(true);
1096   EXPECT_TRUE(send_and_check_show_ime());
1097 
1098   // Set |TextInputState.type| to ui::TEXT_INPUT_TYPE_NONE. Expect no IME.
1099   sender.SetType(ui::TEXT_INPUT_TYPE_NONE);
1100   EXPECT_FALSE(send_and_check_show_ime());
1101 }
1102 #endif  // OS_WIN
1103 
1104 #endif  // USE_AURA
1105 
1106 // Ensure that a cross-process subframe can utilize keyboard edit commands.
1107 // See https://crbug.com/640706.  This test is Linux-specific, as it relies on
1108 // overriding TextEditKeyBindingsDelegateAuraLinux, which only exists on Linux.
1109 #if defined(OS_LINUX) && !defined(OS_CHROMEOS)
IN_PROC_BROWSER_TEST_F(SitePerProcessTextInputManagerTest,SubframeKeyboardEditCommands)1110 IN_PROC_BROWSER_TEST_F(SitePerProcessTextInputManagerTest,
1111                        SubframeKeyboardEditCommands) {
1112   GURL main_url(embedded_test_server()->GetURL(
1113       "a.com", "/frame_tree/page_with_one_frame.html"));
1114   ui_test_utils::NavigateToURL(browser(), main_url);
1115   content::WebContents* web_contents = active_contents();
1116 
1117   GURL frame_url(
1118       embedded_test_server()->GetURL("b.com", "/page_with_input_field.html"));
1119   EXPECT_TRUE(NavigateIframeToURL(web_contents, "child0", frame_url));
1120 
1121   // Focus the subframe and then its input field.  The return value
1122   // "input-focus" will be sent once the input field's focus event fires.
1123   content::RenderFrameHost* child =
1124       ChildFrameAt(web_contents->GetMainFrame(), 0);
1125   std::string result;
1126   std::string script =
1127       "function onInput(e) {"
1128       "  domAutomationController.send(getInputFieldText());"
1129       "}"
1130       "inputField = document.getElementById('text-field');"
1131       "inputField.addEventListener('input', onInput, false);";
1132   EXPECT_TRUE(ExecuteScript(child, script));
1133   EXPECT_TRUE(ExecuteScriptAndExtractString(
1134       child, "window.focus(); focusInputField();", &result));
1135   EXPECT_EQ("input-focus", result);
1136   EXPECT_EQ(child, web_contents->GetFocusedFrame());
1137 
1138   // Generate a couple of keystrokes, which will be routed to the subframe.
1139   content::DOMMessageQueue msg_queue;
1140   std::string reply;
1141   SimulateKeyPress(web_contents, ui::DomKey::FromCharacter('1'),
1142                    ui::DomCode::DIGIT1, ui::VKEY_1, false, false, false, false);
1143   EXPECT_TRUE(msg_queue.WaitForMessage(&reply));
1144   SimulateKeyPress(web_contents, ui::DomKey::FromCharacter('2'),
1145                    ui::DomCode::DIGIT2, ui::VKEY_2, false, false, false, false);
1146   EXPECT_TRUE(msg_queue.WaitForMessage(&reply));
1147 
1148   // Verify that the input field in the subframe received the keystrokes.
1149   EXPECT_TRUE(ExecuteScriptAndExtractString(
1150       child, "window.domAutomationController.send(getInputFieldText());",
1151       &result));
1152   EXPECT_EQ("12", result);
1153 
1154   // Define and install a test delegate that translates any keystroke to a
1155   // command to delete all text from current cursor position to the beginning
1156   // of the line.
1157   class TextDeleteDelegate : public ui::TextEditKeyBindingsDelegateAuraLinux {
1158    public:
1159     TextDeleteDelegate() {}
1160     ~TextDeleteDelegate() override {}
1161 
1162     bool MatchEvent(
1163         const ui::Event& event,
1164         std::vector<ui::TextEditCommandAuraLinux>* commands) override {
1165       if (commands) {
1166         commands->emplace_back(ui::TextEditCommand::DELETE_TO_BEGINNING_OF_LINE,
1167                                "");
1168       }
1169       return true;
1170     }
1171 
1172    private:
1173     DISALLOW_COPY_AND_ASSIGN(TextDeleteDelegate);
1174   };
1175 
1176   TextDeleteDelegate delegate;
1177   ui::TextEditKeyBindingsDelegateAuraLinux* old_delegate =
1178       ui::GetTextEditKeyBindingsDelegate();
1179   ui::SetTextEditKeyBindingsDelegate(&delegate);
1180 
1181   // Press ctrl-alt-shift-D.  The test's delegate will pretend that this
1182   // corresponds to the command to delete everyting to the beginning of the
1183   // line.  Note the use of SendKeyPressSync instead of SimulateKeyPress, as
1184   // the latter doesn't go through
1185   // RenderWidgetHostViewAura::ForwardKeyboardEvent, which contains the edit
1186   // commands logic that's tested here.
1187   ASSERT_TRUE(ui_test_utils::SendKeyPressSync(browser(), ui::VKEY_D, true, true,
1188                                               true, false));
1189   ui::SetTextEditKeyBindingsDelegate(old_delegate);
1190 
1191   // Verify that the input field in the subframe is erased.
1192   EXPECT_TRUE(ExecuteScriptAndExtractString(
1193       child, "window.domAutomationController.send(getInputFieldText());",
1194       &result));
1195   EXPECT_EQ("", result);
1196 }
1197 #endif
1198 
1199 // Ideally, the following code + test should be live in
1200 // 'site_per_process_mac_browsertest.mm'. However, the test
1201 // 'LookUpStringForRangeRoutesToFocusedWidget' relies on an override in
1202 // ContentBrowserClient to register its filters in time. In content shell, we
1203 // cannot have two instances of ShellContentBrowserClient (due to a DCHECK in
1204 // the ctor). Therefore, we put the test here to use ChromeContentBrowserClient
1205 // which does not have the same singleton constraint.
1206 #if defined(OS_MAC)
1207 class ShowDefinitionForWordObserver
1208     : content::RenderWidgetHostViewCocoaObserver {
1209  public:
ShowDefinitionForWordObserver(content::WebContents * web_contents)1210   explicit ShowDefinitionForWordObserver(content::WebContents* web_contents)
1211       : content::RenderWidgetHostViewCocoaObserver(web_contents) {}
1212 
~ShowDefinitionForWordObserver()1213   ~ShowDefinitionForWordObserver() override {}
1214 
WaitForWordLookUp()1215   const std::string& WaitForWordLookUp() {
1216     if (did_receive_string_)
1217       return word_;
1218 
1219     run_loop_.reset(new base::RunLoop());
1220     run_loop_->Run();
1221     return word_;
1222   }
1223 
1224  private:
OnShowDefinitionForAttributedString(const std::string & for_word)1225   void OnShowDefinitionForAttributedString(
1226       const std::string& for_word) override {
1227     did_receive_string_ = true;
1228     word_ = for_word;
1229     if (run_loop_)
1230       run_loop_->Quit();
1231   }
1232 
1233   bool did_receive_string_ = false;
1234   std::string word_;
1235   std::unique_ptr<base::RunLoop> run_loop_;
1236 
1237   DISALLOW_COPY_AND_ASSIGN(ShowDefinitionForWordObserver);
1238 };
1239 
1240 // Flakey (https:://crbug.com/874417).
1241 // This test verifies that requests for dictionary lookup based on selection
1242 // range are routed to the focused RenderWidgetHost.
IN_PROC_BROWSER_TEST_F(SitePerProcessTextInputManagerTest,DISABLED_LookUpStringForRangeRoutesToFocusedWidget)1243 IN_PROC_BROWSER_TEST_F(SitePerProcessTextInputManagerTest,
1244                        DISABLED_LookUpStringForRangeRoutesToFocusedWidget) {
1245   CreateIframePage("a(b)");
1246   std::vector<content::RenderFrameHost*> frames{GetFrame(IndexVector{}),
1247                                                 GetFrame(IndexVector{0})};
1248   std::string expected_words[] = {"word1", "word2"};
1249 
1250   // For each frame, add <input>, set its value to expected word, select it, ask
1251   // for dictionary and verify the word returned from renderer matches.
1252   for (size_t i = 0; i < frames.size(); ++i) {
1253     AddInputFieldToFrame(frames[i], "text", expected_words[i].c_str(), true);
1254     // Focusing the <input> automatically selects the text.
1255     ASSERT_TRUE(
1256         ExecuteScript(frames[i], "document.querySelector('input').focus();"));
1257     ShowDefinitionForWordObserver word_lookup_observer(active_contents());
1258     // Request for the dictionary lookup and intercept the word on its way back.
1259     // The request is always on the tab's view which is a
1260     // RenderWidgetHostViewMac.
1261     content::AskForLookUpDictionaryForRange(
1262         active_contents()->GetRenderWidgetHostView(),
1263         gfx::Range(0, expected_words[i].size()));
1264     EXPECT_EQ(expected_words[i], word_lookup_observer.WaitForWordLookUp());
1265   }
1266 }
1267 
1268 // This test verifies that when a word lookup result comes from the renderer
1269 // after the target RenderWidgetHost has been deleted, the browser will not
1270 // crash. This test covers the case where the target RenderWidgetHost is that of
1271 // an OOPIF.
IN_PROC_BROWSER_TEST_F(SitePerProcessTextInputManagerTest,DoNotCrashBrowserInWordLookUpForDestroyedWidget_ChildFrame)1272 IN_PROC_BROWSER_TEST_F(
1273     SitePerProcessTextInputManagerTest,
1274     DoNotCrashBrowserInWordLookUpForDestroyedWidget_ChildFrame) {
1275   std::unique_ptr<content::WebContents> new_contents =
1276       content::WebContents::Create(content::WebContents::CreateParams(
1277           active_contents()->GetBrowserContext(), nullptr));
1278   content::WebContents* raw_new_contents = new_contents.get();
1279   browser()->tab_strip_model()->InsertWebContentsAt(1, std::move(new_contents),
1280                                                     TabStripModel::ADD_ACTIVE);
1281   EXPECT_EQ(active_contents(), raw_new_contents);
1282 
1283   // Simple page with 1 cross origin (out-of-process) <iframe>.
1284   CreateIframePage("a(b)");
1285 
1286   content::RenderFrameHost* child_frame = GetFrame(IndexVector{0});
1287   // Now add an <input> field and select its text.
1288   AddInputFieldToFrame(child_frame, "text", "four", true);
1289   EXPECT_TRUE(ExecuteScript(child_frame,
1290                             "window.focus();"
1291                             "document.querySelector('input').focus();"
1292                             "document.querySelector('input').select();"));
1293 
1294   content::TextInputTestLocalFrame text_input_local_frame;
1295   text_input_local_frame.SetUp(child_frame);
1296 
1297   // We need to wait for test scenario to complete before leaving this block.
1298   base::RunLoop test_complete_waiter;
1299 
1300   // Destroy the RenderWidgetHost from the browser side right after the
1301   // dictionary message is received. The destruction is post tasked to UI
1302   // thread.
1303   int32_t child_process_id = child_frame->GetProcess()->GetID();
1304   int32_t child_frame_routing_id = child_frame->GetRoutingID();
1305 
1306   text_input_local_frame.SetStringForRangeCallback(base::Bind(
1307       [](int32_t process_id, int32_t routing_id,
1308          const base::Closure& callback_on_io) {
1309         // This runs before TextInputClientMac gets to handle the mojo message.
1310         // Then, by the time TextInputClientMac calls back into UI to show the
1311         // dictionary, the target RWH is already destroyed which will be a
1312         // close enough repro for the crash in https://crbug.com/737032.
1313         ASSERT_TRUE(content::DestroyRenderWidgetHost(process_id, routing_id));
1314 
1315         // Quit the run loop on IO to make sure the message handler of
1316         // TextInputClientMac has successfully run on UI thread.
1317         content::GetIOThreadTaskRunner({})->PostTask(FROM_HERE, callback_on_io);
1318       },
1319       child_process_id, child_frame_routing_id,
1320       test_complete_waiter.QuitClosure()));
1321 
1322   content::RenderWidgetHostView* page_rwhv =
1323       content::WebContents::FromRenderFrameHost(child_frame)
1324           ->GetRenderWidgetHostView();
1325 
1326   // The dictionary request to be made will be routed to the focused frame.
1327   ASSERT_EQ(child_frame, raw_new_contents->GetFocusedFrame());
1328 
1329   // Request for the dictionary lookup and intercept the word on its way back.
1330   // The request is always on the tab's view which is a RenderWidgetHostViewMac.
1331   content::AskForLookUpDictionaryForRange(page_rwhv, gfx::Range(0, 4));
1332 
1333   test_complete_waiter.Run();
1334 }
1335 
1336 // This test verifies that when a word lookup result comes from the renderer
1337 // after the target RenderWidgetHost has been deleted, the browser will not
1338 // crash. This test covers the case where the target RenderWidgetHost is that of
1339 // the main frame (no OOPIFs on page).
IN_PROC_BROWSER_TEST_F(SitePerProcessTextInputManagerTest,DoNotCrashBrowserInWordLookUpForDestroyedWidget_MainFrame)1340 IN_PROC_BROWSER_TEST_F(
1341     SitePerProcessTextInputManagerTest,
1342     DoNotCrashBrowserInWordLookUpForDestroyedWidget_MainFrame) {
1343   std::unique_ptr<content::WebContents> new_contents =
1344       content::WebContents::Create(content::WebContents::CreateParams(
1345           active_contents()->GetBrowserContext(), nullptr));
1346   content::WebContents* raw_new_contents = new_contents.get();
1347   browser()->tab_strip_model()->InsertWebContentsAt(1, std::move(new_contents),
1348                                                     TabStripModel::ADD_ACTIVE);
1349   EXPECT_EQ(active_contents(), raw_new_contents);
1350 
1351   // Simple page with no <iframe>s.
1352   CreateIframePage("a()");
1353 
1354   content::RenderFrameHost* main_frame = GetFrame(IndexVector{});
1355   // Now add an <input> field and select its text.
1356   AddInputFieldToFrame(main_frame, "text", "four", true);
1357   EXPECT_TRUE(ExecuteScript(main_frame,
1358                             "document.querySelector('input').focus();"
1359                             "document.querySelector('input').select();"));
1360 
1361   content::TextInputTestLocalFrame text_input_local_frame;
1362   text_input_local_frame.SetUp(main_frame);
1363 
1364   content::RenderWidgetHostView* page_rwhv = main_frame->GetView();
1365 
1366   // We need to wait for test scenario to complete before leaving this block.
1367   base::RunLoop test_complete_waiter;
1368 
1369   // Destroy the RenderWidgetHost from the browser side right after the
1370   // dictionary message is received. The destruction is post tasked to UI
1371   // thread.
1372   int32_t main_frame_process_id = main_frame->GetProcess()->GetID();
1373   int32_t main_frame_routing_id = main_frame->GetRoutingID();
1374   text_input_local_frame.SetStringForRangeCallback(base::Bind(
1375       [](int32_t process_id, int32_t routing_id,
1376          const base::Closure& callback_on_io) {
1377         // This runs before TextInputClientMac gets to handle the mojo message.
1378         // Then, by the time TextInputClientMac calls back into UI to show the
1379         // dictionary, the target RWH is already destroyed which will be a
1380         // close enough repro for the crash in https://crbug.com/737032.
1381         ASSERT_TRUE(content::DestroyRenderWidgetHost(process_id, routing_id));
1382 
1383         // Quit the run loop on IO to make sure the message handler of
1384         // TextInputClientMac has successfully run on UI thread.
1385         content::GetIOThreadTaskRunner({})->PostTask(FROM_HERE, callback_on_io);
1386       },
1387       main_frame_process_id, main_frame_routing_id,
1388       test_complete_waiter.QuitClosure()));
1389 
1390   // Request for the dictionary lookup and intercept the word on its way back.
1391   // The request is always on the tab's view which is a RenderWidgetHostViewMac.
1392   content::AskForLookUpDictionaryForRange(page_rwhv, gfx::Range(0, 4));
1393 
1394   test_complete_waiter.Run();
1395 }
1396 #endif  //  defined(MAC_OSX)
1397 
1398 #endif  // !defined(OS_ANDROID)
1399