Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / content / browser / frame_host / interstitial_page_impl_browsertest.cc
blob405b3b17ea1a87b176d91ac0e7d4844762be908d
1 // Copyright 2015 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.
5 #include "content/browser/frame_host/interstitial_page_impl.h"
7 #include "base/strings/utf_string_conversions.h"
8 #include "base/synchronization/waitable_event.h"
9 #include "content/browser/web_contents/web_contents_impl.h"
10 #include "content/common/clipboard_messages.h"
11 #include "content/common/frame_messages.h"
12 #include "content/public/browser/browser_message_filter.h"
13 #include "content/public/browser/interstitial_page_delegate.h"
14 #include "content/public/test/browser_test_utils.h"
15 #include "content/public/test/content_browser_test.h"
16 #include "content/public/test/test_utils.h"
17 #include "content/shell/browser/shell.h"
18 #include "ipc/message_filter.h"
19 #include "ui/base/clipboard/scoped_clipboard_writer.h"
20 #include "ui/base/test/test_clipboard.h"
22 namespace content {
24 namespace {
26 class TestInterstitialPageDelegate : public InterstitialPageDelegate {
27 private:
28 // InterstitialPageDelegate:
29 std::string GetHTMLContents() override {
30 return "<html>"
31 "<head>"
32 "<script>"
33 "function create_input_and_set_text(text) {"
34 " var input = document.createElement('input');"
35 " input.id = 'input';"
36 " document.body.appendChild(input);"
37 " document.getElementById('input').value = text;"
38 " input.addEventListener('input',"
39 " function() { document.title='TEXT_CHANGED'; });"
40 "}"
41 "function focus_select_input() {"
42 " document.getElementById('input').select();"
43 "}"
44 "function get_input_text() {"
45 " window.domAutomationController.send("
46 " document.getElementById('input').value);"
47 "}"
48 "function get_selection() {"
49 " window.domAutomationController.send("
50 " window.getSelection().toString());"
51 "}"
52 "</script>"
53 "</head>"
54 "<body>original body text</body>"
55 "</html>";
59 // A title watcher for interstitial pages. The existing TitleWatcher does not
60 // work for interstitial pages. Note that this title watcher waits for the
61 // title update IPC message not the actual title update. So, the new title is
62 // probably not propagated completely, yet.
63 class InterstitialTitleUpdateWatcher : public BrowserMessageFilter {
64 public:
65 explicit InterstitialTitleUpdateWatcher(InterstitialPage* interstitial)
66 : BrowserMessageFilter(FrameMsgStart) {
67 interstitial->GetMainFrame()->GetProcess()->AddFilter(this);
70 void InitWait(const std::string& expected_title) {
71 DCHECK(!run_loop_);
72 expected_title_ = base::UTF8ToUTF16(expected_title);
73 run_loop_.reset(new base::RunLoop());
76 void Wait() {
77 DCHECK(run_loop_);
78 run_loop_->Run();
79 run_loop_.reset();
82 private:
83 ~InterstitialTitleUpdateWatcher() override {}
85 void OnTitleUpdateReceived(const base::string16& title) {
86 DCHECK(run_loop_);
87 if (title == expected_title_)
88 run_loop_->Quit();
91 // BrowserMessageFilter:
92 bool OnMessageReceived(const IPC::Message& message) override {
93 if (!run_loop_)
94 return false;
96 if (message.type() == FrameHostMsg_UpdateTitle::ID) {
97 FrameHostMsg_UpdateTitle::Param params;
98 if (FrameHostMsg_UpdateTitle::Read(&message, &params)) {
99 BrowserThread::PostTask(
100 BrowserThread::UI, FROM_HERE,
101 base::Bind(&InterstitialTitleUpdateWatcher::OnTitleUpdateReceived,
102 this, base::get<0>(params)));
105 return false;
108 base::string16 expected_title_;
109 scoped_ptr<base::RunLoop> run_loop_;
111 DISALLOW_COPY_AND_ASSIGN(InterstitialTitleUpdateWatcher);
114 // A message filter that watches for WriteText and CommitWrite clipboard IPC
115 // messages to make sure cut/copy is working properly. It will mark these events
116 // as handled to prevent modification of the actual clipboard.
117 class ClipboardMessageWatcher : public IPC::MessageFilter {
118 public:
119 explicit ClipboardMessageWatcher(InterstitialPage* interstitial) {
120 interstitial->GetMainFrame()->GetProcess()->GetChannel()->AddFilter(this);
123 void InitWait() {
124 DCHECK(!run_loop_);
125 run_loop_.reset(new base::RunLoop());
128 void WaitForWriteCommit() {
129 DCHECK(run_loop_);
130 run_loop_->Run();
131 run_loop_.reset();
134 const std::string& last_text() const { return last_text_; }
136 private:
137 ~ClipboardMessageWatcher() override {}
139 void OnWriteText(const std::string& text) { last_text_ = text; }
141 void OnCommitWrite() {
142 DCHECK(run_loop_);
143 run_loop_->Quit();
146 // IPC::MessageFilter:
147 bool OnMessageReceived(const IPC::Message& message) override {
148 if (!run_loop_)
149 return false;
151 if (message.type() == ClipboardHostMsg_WriteText::ID) {
152 ClipboardHostMsg_WriteText::Param params;
153 if (ClipboardHostMsg_WriteText::Read(&message, &params)) {
154 BrowserThread::PostTask(
155 BrowserThread::UI, FROM_HERE,
156 base::Bind(&ClipboardMessageWatcher::OnWriteText, this,
157 base::UTF16ToUTF8(base::get<1>(params))));
159 return true;
161 if (message.type() == ClipboardHostMsg_CommitWrite::ID) {
162 BrowserThread::PostTask(
163 BrowserThread::UI, FROM_HERE,
164 base::Bind(&ClipboardMessageWatcher::OnCommitWrite, this));
165 return true;
167 return false;
170 scoped_ptr<base::RunLoop> run_loop_;
171 std::string last_text_;
173 DISALLOW_COPY_AND_ASSIGN(ClipboardMessageWatcher);
176 } // namespace
178 class InterstitialPageImplTest : public ContentBrowserTest {
179 public:
180 InterstitialPageImplTest() {}
182 ~InterstitialPageImplTest() override {}
184 protected:
185 void SetUpTestClipboard() {
186 #if defined(OS_WIN)
187 // On Windows, clipboard reads are handled on the IO thread. So, the test
188 // clipboard should be created for the IO thread.
189 if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) {
190 RunTaskOnIOThreadAndWait(
191 base::Bind(&InterstitialPageImplTest::SetUpTestClipboard, this));
192 return;
194 #endif
195 ui::TestClipboard::CreateForCurrentThread();
198 void TearDownTestClipboard() {
199 #if defined(OS_WIN)
200 // On Windows, test clipboard is created for the IO thread. So, destroy it
201 // for the IO thread, too.
202 if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) {
203 RunTaskOnIOThreadAndWait(
204 base::Bind(&InterstitialPageImplTest::TearDownTestClipboard, this));
205 return;
207 #endif
208 ui::Clipboard::DestroyClipboardForCurrentThread();
211 void SetClipboardText(const std::string& text) {
212 #if defined(OS_WIN)
213 // On Windows, clipboard reads are handled on the IO thread. So, set the
214 // text for the IO thread clipboard.
215 if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) {
216 RunTaskOnIOThreadAndWait(
217 base::Bind(&InterstitialPageImplTest::SetClipboardText, this, text));
218 return;
220 #endif
221 ui::ScopedClipboardWriter clipboard_writer(ui::CLIPBOARD_TYPE_COPY_PASTE);
222 clipboard_writer.WriteText(base::ASCIIToUTF16(text));
225 void SetUpInterstitialPage() {
226 WebContentsImpl* web_contents =
227 static_cast<WebContentsImpl*>(shell()->web_contents());
229 // Create the interstitial page.
230 TestInterstitialPageDelegate* interstitial_delegate =
231 new TestInterstitialPageDelegate;
232 GURL url("http://interstitial");
233 interstitial_.reset(new InterstitialPageImpl(
234 web_contents, static_cast<RenderWidgetHostDelegate*>(web_contents),
235 true, url, interstitial_delegate));
236 interstitial_->Show();
237 WaitForInterstitialAttach(web_contents);
239 // Focus the interstitial frame
240 FrameTree* frame_tree = static_cast<RenderViewHostDelegate*>(
241 interstitial_.get())->GetFrameTree();
242 frame_tree->SetFocusedFrame(frame_tree->root());
244 clipboard_message_watcher_ =
245 new ClipboardMessageWatcher(interstitial_.get());
246 title_update_watcher_ =
247 new InterstitialTitleUpdateWatcher(interstitial_.get());
249 // Wait until page loads completely.
250 ASSERT_TRUE(WaitForRenderFrameReady(interstitial_->GetMainFrame()));
253 void TearDownInterstitialPage() {
254 // Close the interstitial.
255 interstitial_->DontProceed();
256 WaitForInterstitialDetach(shell()->web_contents());
257 interstitial_.reset();
260 bool FocusInputAndSelectText() {
261 return ExecuteScript(interstitial_->GetMainFrame(), "focus_select_input()");
264 bool GetInputText(std::string* input_text) {
265 return ExecuteScriptAndExtractString(interstitial_->GetMainFrame(),
266 "get_input_text()", input_text);
269 bool GetSelection(std::string* input_text) {
270 return ExecuteScriptAndExtractString(interstitial_->GetMainFrame(),
271 "get_selection()", input_text);
274 bool CreateInputAndSetText(const std::string& text) {
275 return ExecuteScript(interstitial_->GetMainFrame(),
276 "create_input_and_set_text('" + text + "')");
279 std::string PerformCut() {
280 clipboard_message_watcher_->InitWait();
281 title_update_watcher_->InitWait("TEXT_CHANGED");
282 RenderFrameHostImpl* rfh =
283 static_cast<RenderFrameHostImpl*>(interstitial_->GetMainFrame());
284 rfh->GetRenderWidgetHost()->delegate()->Cut();
285 clipboard_message_watcher_->WaitForWriteCommit();
286 title_update_watcher_->Wait();
287 return clipboard_message_watcher_->last_text();
290 std::string PerformCopy() {
291 clipboard_message_watcher_->InitWait();
292 RenderFrameHostImpl* rfh =
293 static_cast<RenderFrameHostImpl*>(interstitial_->GetMainFrame());
294 rfh->GetRenderWidgetHost()->delegate()->Copy();
295 clipboard_message_watcher_->WaitForWriteCommit();
296 return clipboard_message_watcher_->last_text();
299 void PerformPaste() {
300 title_update_watcher_->InitWait("TEXT_CHANGED");
301 RenderFrameHostImpl* rfh =
302 static_cast<RenderFrameHostImpl*>(interstitial_->GetMainFrame());
303 rfh->GetRenderWidgetHost()->delegate()->Paste();
304 title_update_watcher_->Wait();
307 void PerformSelectAll() {
308 RenderFrameHostImpl* rfh =
309 static_cast<RenderFrameHostImpl*>(interstitial_->GetMainFrame());
310 rfh->GetRenderWidgetHost()->delegate()->SelectAll();
313 private:
314 void RunTaskOnIOThreadAndWait(const base::Closure& task) {
315 base::WaitableEvent completion(false, false);
316 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
317 base::Bind(&InterstitialPageImplTest::RunTask, this,
318 task, &completion));
319 completion.Wait();
322 void RunTask(const base::Closure& task, base::WaitableEvent* completion) {
323 task.Run();
324 completion->Signal();
327 scoped_ptr<InterstitialPageImpl> interstitial_;
328 scoped_refptr<ClipboardMessageWatcher> clipboard_message_watcher_;
329 scoped_refptr<InterstitialTitleUpdateWatcher> title_update_watcher_;
331 DISALLOW_COPY_AND_ASSIGN(InterstitialPageImplTest);
334 IN_PROC_BROWSER_TEST_F(InterstitialPageImplTest, Cut) {
335 SetUpInterstitialPage();
337 ASSERT_TRUE(CreateInputAndSetText("text-to-cut"));
338 ASSERT_TRUE(FocusInputAndSelectText());
340 std::string clipboard_text = PerformCut();
341 EXPECT_EQ("text-to-cut", clipboard_text);
343 std::string input_text;
344 ASSERT_TRUE(GetInputText(&input_text));
345 EXPECT_EQ(std::string(), input_text);
347 TearDownInterstitialPage();
350 IN_PROC_BROWSER_TEST_F(InterstitialPageImplTest, Copy) {
351 SetUpInterstitialPage();
353 ASSERT_TRUE(CreateInputAndSetText("text-to-copy"));
354 ASSERT_TRUE(FocusInputAndSelectText());
356 std::string clipboard_text = PerformCopy();
357 EXPECT_EQ("text-to-copy", clipboard_text);
359 std::string input_text;
360 ASSERT_TRUE(GetInputText(&input_text));
361 EXPECT_EQ("text-to-copy", input_text);
363 TearDownInterstitialPage();
366 IN_PROC_BROWSER_TEST_F(InterstitialPageImplTest, Paste) {
367 SetUpTestClipboard();
368 SetUpInterstitialPage();
370 SetClipboardText("text-to-paste");
372 ASSERT_TRUE(CreateInputAndSetText(std::string()));
373 ASSERT_TRUE(FocusInputAndSelectText());
375 PerformPaste();
377 std::string input_text;
378 ASSERT_TRUE(GetInputText(&input_text));
379 EXPECT_EQ("text-to-paste", input_text);
381 TearDownInterstitialPage();
382 TearDownTestClipboard();
385 IN_PROC_BROWSER_TEST_F(InterstitialPageImplTest, SelectAll) {
386 SetUpInterstitialPage();
388 std::string input_text;
389 ASSERT_TRUE(GetSelection(&input_text));
390 EXPECT_EQ(std::string(), input_text);
392 PerformSelectAll();
393 ASSERT_TRUE(GetSelection(&input_text));
394 EXPECT_EQ("original body text", input_text);
396 TearDownInterstitialPage();
399 } // namespace content