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"
26 class TestInterstitialPageDelegate
: public InterstitialPageDelegate
{
28 // InterstitialPageDelegate:
29 std::string
GetHTMLContents() override
{
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'; });"
41 "function focus_select_input() {"
42 " document.getElementById('input').select();"
44 "function get_input_text() {"
45 " window.domAutomationController.send("
46 " document.getElementById('input').value);"
48 "function get_selection() {"
49 " window.domAutomationController.send("
50 " window.getSelection().toString());"
54 "<body>original body text</body>"
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
{
65 explicit InterstitialTitleUpdateWatcher(InterstitialPage
* interstitial
)
66 : BrowserMessageFilter(FrameMsgStart
) {
67 interstitial
->GetMainFrame()->GetProcess()->AddFilter(this);
70 void InitWait(const std::string
& expected_title
) {
72 expected_title_
= base::UTF8ToUTF16(expected_title
);
73 run_loop_
.reset(new base::RunLoop());
83 ~InterstitialTitleUpdateWatcher() override
{}
85 void OnTitleUpdateReceived(const base::string16
& title
) {
87 if (title
== expected_title_
)
91 // BrowserMessageFilter:
92 bool OnMessageReceived(const IPC::Message
& message
) override
{
96 if (message
.type() == FrameHostMsg_UpdateTitle::ID
) {
97 FrameHostMsg_UpdateTitle::Param params
;
98 if (FrameHostMsg_UpdateTitle::Read(&message
, ¶ms
)) {
99 BrowserThread::PostTask(
100 BrowserThread::UI
, FROM_HERE
,
101 base::Bind(&InterstitialTitleUpdateWatcher::OnTitleUpdateReceived
,
102 this, base::get
<0>(params
)));
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
{
119 explicit ClipboardMessageWatcher(InterstitialPage
* interstitial
) {
120 interstitial
->GetMainFrame()->GetProcess()->GetChannel()->AddFilter(this);
125 run_loop_
.reset(new base::RunLoop());
128 void WaitForWriteCommit() {
134 const std::string
& last_text() const { return last_text_
; }
137 ~ClipboardMessageWatcher() override
{}
139 void OnWriteText(const std::string
& text
) { last_text_
= text
; }
141 void OnCommitWrite() {
146 // IPC::MessageFilter:
147 bool OnMessageReceived(const IPC::Message
& message
) override
{
151 if (message
.type() == ClipboardHostMsg_WriteText::ID
) {
152 ClipboardHostMsg_WriteText::Param params
;
153 if (ClipboardHostMsg_WriteText::Read(&message
, ¶ms
)) {
154 BrowserThread::PostTask(
155 BrowserThread::UI
, FROM_HERE
,
156 base::Bind(&ClipboardMessageWatcher::OnWriteText
, this,
157 base::UTF16ToUTF8(base::get
<1>(params
))));
161 if (message
.type() == ClipboardHostMsg_CommitWrite::ID
) {
162 BrowserThread::PostTask(
163 BrowserThread::UI
, FROM_HERE
,
164 base::Bind(&ClipboardMessageWatcher::OnCommitWrite
, this));
170 scoped_ptr
<base::RunLoop
> run_loop_
;
171 std::string last_text_
;
173 DISALLOW_COPY_AND_ASSIGN(ClipboardMessageWatcher
);
178 class InterstitialPageImplTest
: public ContentBrowserTest
{
180 InterstitialPageImplTest() {}
182 ~InterstitialPageImplTest() override
{}
185 void SetUpTestClipboard() {
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));
195 ui::TestClipboard::CreateForCurrentThread();
198 void TearDownTestClipboard() {
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));
208 ui::Clipboard::DestroyClipboardForCurrentThread();
211 void SetClipboardText(const std::string
& text
) {
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
));
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();
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,
322 void RunTask(const base::Closure
& task
, base::WaitableEvent
* completion
) {
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());
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
);
393 ASSERT_TRUE(GetSelection(&input_text
));
394 EXPECT_EQ("original body text", input_text
);
396 TearDownInterstitialPage();
399 } // namespace content