Chromium Code Reviews| Index: chrome/browser/renderer_host/site_per_process_text_input_browsertest.cc |
| diff --git a/chrome/browser/renderer_host/site_per_process_text_input_browsertest.cc b/chrome/browser/renderer_host/site_per_process_text_input_browsertest.cc |
| index 384a4c0b4716f5cb291a3b841530850fed76eb09..70b818c5f4b673f3ce47304fec7cf7814fbb839c 100644 |
| --- a/chrome/browser/renderer_host/site_per_process_text_input_browsertest.cc |
| +++ b/chrome/browser/renderer_host/site_per_process_text_input_browsertest.cc |
| @@ -6,15 +6,19 @@ |
| #include "base/command_line.h" |
| #include "base/strings/utf_string_conversions.h" |
| +#include "chrome/browser/chrome_content_browser_client.h" |
| #include "chrome/browser/ui/browser.h" |
| #include "chrome/browser/ui/tabs/tab_strip_model.h" |
| #include "chrome/test/base/in_process_browser_test.h" |
| #include "chrome/test/base/interactive_test_utils.h" |
| #include "chrome/test/base/ui_test_utils.h" |
| +#include "content/public/browser/browser_message_filter.h" |
| +#include "content/public/browser/content_browser_client.h" |
| #include "content/public/browser/render_frame_host.h" |
| #include "content/public/browser/render_process_host.h" |
| #include "content/public/browser/render_widget_host_view.h" |
| #include "content/public/browser/web_contents.h" |
| +#include "content/public/common/content_client.h" |
| #include "content/public/test/browser_test_utils.h" |
| #include "content/public/test/content_browser_test_utils.h" |
| #include "content/public/test/test_utils.h" |
| @@ -330,7 +334,8 @@ class SitePerProcessTextInputManagerTest : public InProcessBrowserTest { |
| // static |
| // Adds an <input> field to a given frame by executing javascript code. |
| // The input can be added as the first element or the last element of |
| - // |document.body|. |
| + // |document.body|. The text range defined by |selection_range| will be |
| + // marked. |
| static void AddInputFieldToFrame(content::RenderFrameHost* rfh, |
| const std::string& type, |
| const std::string& value, |
| @@ -346,6 +351,25 @@ class SitePerProcessTextInputManagerTest : public InProcessBrowserTest { |
| EXPECT_TRUE(ExecuteScript(rfh, script)); |
| } |
| + // Returns a point representing the top-left corner of the first <input> |
| + // inside |rfh|. |
|
EhsanK
2016/10/11 19:31:56
Apologies. This method should have not been in the
|
| + static gfx::Point GetTopLeftOfFirstInput(content::RenderFrameHost* rfh) { |
| + int x = 0; |
| + int y = 0; |
| + // Get the client X value. |
| + EXPECT_TRUE(content::ExecuteScriptAndExtractInt( |
| + rfh, |
| + "var input = document.querySelector('input');" |
| + "domAutomationController.send(input.getBoundingClientRect().left);", |
| + &x)); |
| + EXPECT_TRUE(content::ExecuteScriptAndExtractInt( |
| + rfh, |
| + "var input = document.querySelector('input');" |
| + "domAutomationController.send(input.getBoundingClientRect().top);", |
| + &y)); |
| + return gfx::Point(x, y); |
| + } |
| + |
| // Uses 'cross_site_iframe_factory.html'. The main frame's domain is |
| // 'a.com'. |
| void CreateIframePage(const std::string& structure) { |
| @@ -948,3 +972,173 @@ IN_PROC_BROWSER_TEST_F(SitePerProcessTextInputManagerTest, |
| EXPECT_EQ("", result); |
| } |
| #endif |
| + |
| +#if defined(OS_MACOSX) |
|
Charlie Reis
2016/10/07 19:07:21
We added a site_per_process_mac_browsertest.mm rec
EhsanK
2016/10/11 19:31:56
Yes, ideally, we should stay within that file. Spe
Charlie Reis
2016/10/17 21:35:03
Ok, sounds like ShellContentBrowserClient was non-
|
| +// An observer of number of open windows. |
| +class WindowCountObserver { |
| + public: |
| + explicit WindowCountObserver(size_t lower_limit) : limit_(lower_limit) {} |
| + ~WindowCountObserver() {} |
| + |
| + // Keep polling the count of open windows until the number exceeds |limit_|. |
| + void WaitForLimitOrMore() { |
| + size_t current_count = content::GetOpenNSWindowsCount(); |
| + if (current_count >= limit_) |
| + return; |
| + |
| + message_loop_runner_ = new content::MessageLoopRunner(); |
| + message_loop_runner_->Run(); |
| + content::BrowserThread::PostTask( |
| + content::BrowserThread::UI, FROM_HERE, |
| + base::Bind(&WindowCountObserver::CheckWindowCount, |
| + base::Unretained(this))); |
| + } |
| + |
| + private: |
| + void CheckWindowCount() { |
| + size_t current_count = content::GetOpenNSWindowsCount(); |
| + if (current_count >= limit_) { |
| + message_loop_runner_->Quit(); |
| + return; |
| + } |
| + content::BrowserThread::PostTask( |
| + content::BrowserThread::UI, FROM_HERE, |
| + base::Bind(&WindowCountObserver::CheckWindowCount, |
| + base::Unretained(this))); |
| + } |
| + |
| + size_t limit_; |
| + scoped_refptr<content::MessageLoopRunner> message_loop_runner_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(WindowCountObserver); |
| +}; |
| + |
| +// The original TextInputClientMessageFilter is added during the initialization |
| +// phase of RenderProcessHost. The only chance we have to add the test filter |
| +// (so that it can receive the TextInputClientMac incoming IPC messages) is |
| +// during the call to RenderProcessWillLaunch() on ContentBrowserClient. This |
| +// class provides that for testing. |
| +class TestBrowserClient : public ChromeContentBrowserClient { |
| + public: |
| + TestBrowserClient() {} |
| + ~TestBrowserClient() override {} |
| + |
| + // ContentBrowserClient overrides. |
| + void RenderProcessWillLaunch( |
| + content::RenderProcessHost* process_host) override { |
| + ChromeContentBrowserClient::RenderProcessWillLaunch(process_host); |
| + filters_.push_back( |
| + new content::TestTextInputClientMessageFilter(process_host)); |
| + } |
| + |
| + // Retrieves the registered filter for the given RenderProcessHost. It will |
| + // return false if the RenderProcessHost was initialized while a different |
| + // instance of ContentBrowserClient was in action. |
| + scoped_refptr<content::TestTextInputClientMessageFilter> |
| + GetTextInputClientMessageFilterForProcess( |
| + content::RenderProcessHost* process_host) const { |
| + for (auto filter : filters_) { |
| + if (filter->process() == process_host) |
| + return filter; |
| + } |
| + return nullptr; |
| + } |
| + |
| + private: |
| + std::vector<scoped_refptr<content::TestTextInputClientMessageFilter>> |
| + filters_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(TestBrowserClient); |
| +}; |
| + |
| +// This test verifies that requests for dictionary lookup based on selection |
| +// range are routed to the focused RenderWidgetHost. |
| +IN_PROC_BROWSER_TEST_F(SitePerProcessTextInputManagerTest, |
| + LookUpStringForRangeRoutesToFocusedWidget) { |
| + // TestBrowserClient needs to replace the ChromeContenBrowserClient after most |
| + // things are initialized but before the WebContents is created. Here we make |
| + // that happen by creating a new WebContents in a new tab. But before the test |
| + // exits, we must destroy the contents and replace the old |
| + // ContentBrowserClient because the original WebContents and the new one have |
| + // been initialized with the original ContentBrowserClient and the new |
| + // TestBrowserClient, respectively. |
| + TestBrowserClient browser_client; |
| + content::ContentBrowserClient* old_browser_client = |
| + content::SetBrowserClientForTesting(&browser_client); |
| + |
| + content::WebContents* new_contents = |
| + content::WebContents::Create(content::WebContents::CreateParams( |
| + active_contents()->GetBrowserContext(), nullptr)); |
| + browser()->tab_strip_model()->InsertWebContentsAt(1, new_contents, |
| + TabStripModel::ADD_ACTIVE); |
| + EXPECT_EQ(active_contents(), new_contents); |
| + |
| + // Starting the test body. |
| + CreateIframePage("a(b)"); |
| + std::vector<content::RenderFrameHost*> frames{GetFrame(IndexVector{}), |
| + GetFrame(IndexVector{0})}; |
| + std::vector<content::RenderWidgetHostView*> views; |
| + for (auto frame : frames) |
| + views.push_back(frame->GetView()); |
| + std::vector<std::string> values{"main frame", "child frame"}; |
| + |
| + // Adding some field with text to each frame so that we can later query for |
| + // dictionary lookup. |
| + for (size_t i = 0; i < frames.size(); ++i) |
| + AddInputFieldToFrame(frames[i], "text", values[i], true); |
| + |
| + std::string result; |
| + // Recording window count now so that our WindowCountObserver will detect the |
| + // popup window. |
| + size_t current_window_count = content::GetOpenNSWindowsCount(); |
| + |
| + // Ensure we have both focus and selected text inside the main frame. |
| + EXPECT_TRUE( |
| + ExecuteScript(frames[0], "document.querySelector('input').focus();")); |
| + |
| + // Request for the dictionary lookup and intercept the word on its way back. |
| + // The request is always on the tab's view which is a RenderWidgetHostViewMac. |
| + content::AskForLookUpDictoinaryForRange(views[0], gfx::Range(0, 4)); |
| + |
| + // Wait until the result comes back. |
| + auto root_filter = browser_client.GetTextInputClientMessageFilterForProcess( |
| + frames[0]->GetProcess()); |
| + EXPECT_TRUE(root_filter); |
| + root_filter->WaitForStringFromRange(); |
| + |
| + EXPECT_EQ("main", root_filter->string_from_range()); |
| + |
| + // Wait for the popup to appear to make sure TextInputClientMac has consumed |
| + // the reply handler for the previous request. |
| + WindowCountObserver(current_window_count).WaitForLimitOrMore(); |
| + |
| + // Ensure we have both focus and selected text inside the child frame. |
| + EXPECT_TRUE( |
| + ExecuteScript(frames[1], "document.querySelector('input').focus();")); |
| + |
| + // Record window count again for the popup observer. |
| + current_window_count = content::GetOpenNSWindowsCount(); |
| + |
| + // Make another request. |
| + content::AskForLookUpDictoinaryForRange(views[0], gfx::Range(0, 5)); |
| + |
| + // Wait until the result comes back. |
| + auto child_filter = browser_client.GetTextInputClientMessageFilterForProcess( |
| + frames[1]->GetProcess()); |
| + child_filter->WaitForStringFromRange(); |
| + |
| + EXPECT_EQ("child", child_filter->string_from_range()); |
| + |
| + // Wait for the popup to appear to make sure TextInputClientMac has consumed |
| + // the reply handler for the previous request. |
| + WindowCountObserver(current_window_count).WaitForLimitOrMore(); |
| + // Test ends here. The rest is cleanup. |
| + |
| + // Closing this WebContents while we still hold on to our TestBrowserClient. |
| + EXPECT_TRUE(browser()->tab_strip_model()->CloseWebContentsAt( |
| + 1, TabStripModel::CLOSE_USER_GESTURE)); |
| + |
| + // For the cleanup of the original WebContents in tab index 0. |
| + content::SetBrowserClientForTesting(old_browser_client); |
| +} |
| +#endif |