Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(616)

Side by Side Diff: content/browser/frame_host/interstitial_page_impl_browsertest.cc

Issue 1162373002: Make some editing/selection functions accessible to c/b/renderer_host/ (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Simplified wait implementations Created 5 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
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.
4
5 #include "content/browser/frame_host/interstitial_page_impl.h"
6
7 #include "base/auto_reset.h"
dcheng 2015/07/02 07:06:25 This is no longer needed.
mohsen 2015/07/02 16:41:01 Thanks for catching this. Removed.
8 #include "base/strings/utf_string_conversions.h"
9 #include "base/synchronization/waitable_event.h"
10 #include "content/browser/web_contents/web_contents_impl.h"
11 #include "content/common/clipboard_messages.h"
12 #include "content/common/frame_messages.h"
13 #include "content/public/browser/browser_message_filter.h"
14 #include "content/public/browser/interstitial_page_delegate.h"
15 #include "content/public/test/browser_test_utils.h"
16 #include "content/public/test/content_browser_test.h"
17 #include "content/public/test/test_utils.h"
18 #include "content/shell/browser/shell.h"
19 #include "ipc/message_filter.h"
20 #include "ui/base/clipboard/scoped_clipboard_writer.h"
21 #include "ui/base/test/test_clipboard.h"
22
23 namespace content {
24
25 namespace {
26
27 class TestInterstitialPageDelegate : public InterstitialPageDelegate {
28 private:
29 // InterstitialPageDelegate:
30 std::string GetHTMLContents() override {
31 return "<html>"
32 "<head>"
33 "<title>LOADED</title>"
34 "<script>"
35 "function focus_select_input() {"
36 " document.getElementById('input').select();"
37 "}"
38 "function set_input_text(text) {"
39 " document.getElementById('input').value = text;"
40 "}"
41 "function get_input_text() {"
42 " window.domAutomationController.send("
43 " document.getElementById('input').value);"
44 "}"
45 "</script>"
46 "</head>"
47 "<body>"
48 " <input id='input' oninput='document.title=\"TEXT_CHANGED\"'>"
49 "</body>"
50 "</html>";
51 }
52 };
53
54 // A title watcher for interstitial pages. The existing TitleWatcher does not
55 // work for interstitial pages. Note that this title watcher waits for the
56 // title update IPC message not the actual title update. So, the new title is
57 // probably not propagated completely, yet.
58 class InterstitialTitleUpdateWatcher : public BrowserMessageFilter {
59 public:
60 explicit InterstitialTitleUpdateWatcher(InterstitialPage* interstitial)
61 : BrowserMessageFilter(FrameMsgStart) {
62 interstitial->GetMainFrame()->GetProcess()->AddFilter(this);
63 }
64
65 void InitWait(const std::string& expected_title) {
66 DCHECK(!run_loop_);
67 expected_title_ = base::ASCIIToUTF16(expected_title);
dcheng 2015/07/02 07:06:25 Nit: UTF8ToUTF16 is probably a bit more future pro
mohsen 2015/07/02 16:41:01 Done.
68 run_loop_.reset(new base::RunLoop());
69 }
70
71 void Wait() {
72 DCHECK(run_loop_);
73 run_loop_->Run();
74 run_loop_.reset();
75 }
76
77 private:
78 ~InterstitialTitleUpdateWatcher() override {}
79
80 void OnTitleUpdateReceived(const base::string16& title) {
81 DCHECK(run_loop_);
82 if (title == expected_title_)
83 run_loop_->Quit();
84 }
85
86 // BrowserMessageFilter:
87 bool OnMessageReceived(const IPC::Message& message) override {
88 if (!run_loop_)
89 return false;
90
91 if (message.type() == FrameHostMsg_UpdateTitle::ID) {
92 FrameHostMsg_UpdateTitle::Param params;
93 if (FrameHostMsg_UpdateTitle::Read(&message, &params)) {
94 BrowserThread::PostTask(
95 BrowserThread::UI, FROM_HERE,
96 base::Bind(&InterstitialTitleUpdateWatcher::OnTitleUpdateReceived,
97 this, base::get<0>(params)));
98 }
99 }
100 return false;
101 }
102
103 base::string16 expected_title_;
104 scoped_ptr<base::RunLoop> run_loop_;
105
106 DISALLOW_COPY_AND_ASSIGN(InterstitialTitleUpdateWatcher);
107 };
108
109 // A message filter that watches for WriteText and CommitWrite clipboard IPC
110 // messages to make sure cut/copy is working properly. It will mark these events
111 // as handled to prevent modification of the actual clipboard.
112 class ClipboardMessageWatcher : public IPC::MessageFilter {
113 public:
114 explicit ClipboardMessageWatcher(InterstitialPage* interstitial) {
115 interstitial->GetMainFrame()->GetProcess()->GetChannel()->AddFilter(this);
116 }
117
118 void InitWait() {
119 DCHECK(!run_loop_);
120 run_loop_.reset(new base::RunLoop());
121 }
122
123 void WaitForWriteCommit() {
124 DCHECK(run_loop_);
125 run_loop_->Run();
126 run_loop_.reset();
127 }
128
129 const base::string16& last_text() const { return last_text_; }
130
131 private:
132 ~ClipboardMessageWatcher() override {}
133
134 void OnWriteText(const base::string16& text) { last_text_ = text; }
135
136 void OnCommitWrite() {
137 DCHECK(run_loop_);
138 run_loop_->Quit();
139 }
140
141 // IPC::MessageFilter:
142 bool OnMessageReceived(const IPC::Message& message) override {
143 if (!run_loop_)
144 return false;
145
146 if (message.type() == ClipboardHostMsg_WriteText::ID) {
147 ClipboardHostMsg_WriteText::Param params;
148 if (ClipboardHostMsg_WriteText::Read(&message, &params)) {
149 BrowserThread::PostTask(
150 BrowserThread::UI, FROM_HERE,
151 base::Bind(&ClipboardMessageWatcher::OnWriteText, this,
152 base::get<1>(params)));
153 }
154 return true;
155 }
156 if (message.type() == ClipboardHostMsg_CommitWrite::ID) {
157 BrowserThread::PostTask(
158 BrowserThread::UI, FROM_HERE,
159 base::Bind(&ClipboardMessageWatcher::OnCommitWrite, this));
160 return true;
161 }
162 return false;
163 }
164
165 scoped_ptr<base::RunLoop> run_loop_;
166 base::string16 last_text_;
167
168 DISALLOW_COPY_AND_ASSIGN(ClipboardMessageWatcher);
169 };
170
171 } // namespace
172
173 class InterstitialPageImplTest : public ContentBrowserTest {
174 public:
175 InterstitialPageImplTest()
176 : clipboard_message_watcher_(nullptr), title_update_watcher_(nullptr) {}
177
178 ~InterstitialPageImplTest() override {}
179
180 protected:
181 void SetUpTestClipboard() {
182 #if defined(OS_WIN)
183 // On Windows, clipboard reads are handled on the IO thread. So, the test
184 // clipboard should be created for the IO thread.
185 if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) {
186 RunTaskOnIOThreadAndWait(
187 base::Bind(&InterstitialPageImplTest::SetUpTestClipboard, this));
188 return;
189 }
190 #endif
191 ui::TestClipboard::CreateForCurrentThread();
192 }
193
194 void TearDownTestClipboard() {
195 #if defined(OS_WIN)
196 // On Windows, test clipboard is created for the IO thread. So, destroy it
197 // for the IO thread, too.
198 if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) {
199 RunTaskOnIOThreadAndWait(
200 base::Bind(&InterstitialPageImplTest::TearDownTestClipboard, this));
201 return;
202 }
203 #endif
204 ui::Clipboard::DestroyClipboardForCurrentThread();
205 }
206
207 void SetClipboardText(const std::string& text) {
208 #if defined(OS_WIN)
209 // On Windows, clipboard reads are handled on the IO thread. So, set the
210 // text for the IO thread clipboard.
211 if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) {
212 RunTaskOnIOThreadAndWait(
213 base::Bind(&InterstitialPageImplTest::SetClipboardText, this, text));
214 return;
215 }
216 #endif
217 ui::ScopedClipboardWriter clipboard_writer(ui::CLIPBOARD_TYPE_COPY_PASTE);
218 clipboard_writer.WriteText(base::ASCIIToUTF16(text));
219 }
220
221 void SetUpInterstitialPage() {
222 WebContentsImpl* web_contents =
223 static_cast<WebContentsImpl*>(shell()->web_contents());
224
225 // Create the interstitial page.
226 TestInterstitialPageDelegate* interstitial_delegate =
227 new TestInterstitialPageDelegate;
228 GURL url("http://interstitial");
229 interstitial_.reset(new InterstitialPageImpl(
230 web_contents, static_cast<RenderWidgetHostDelegate*>(web_contents),
231 true, url, interstitial_delegate));
232 interstitial_->Show();
233 WaitForInterstitialAttach(web_contents);
234
235 // Focus the interstitial frame
236 FrameTree* frame_tree = static_cast<RenderViewHostDelegate*>(
237 interstitial_.get())->GetFrameTree();
238 frame_tree->SetFocusedFrame(frame_tree->root());
239
240 clipboard_message_watcher_ =
241 new ClipboardMessageWatcher(interstitial_.get());
242 title_update_watcher_ =
243 new InterstitialTitleUpdateWatcher(interstitial_.get());
dcheng 2015/07/02 07:06:25 These are leaked right now. Will the bots complain
mohsen 2015/07/02 16:41:01 My understanding is that these are ref-counted and
dcheng 2015/07/02 16:53:28 I suppose that's right, but it wasn't obvious to m
mohsen 2015/07/02 17:03:45 Commented this in code.
mohsen 2015/07/02 17:04:53 I mean documented...
244
245 // Wait until page loads completely.
246 if (web_contents->GetTitle() != base::ASCIIToUTF16("LOADED")) {
247 title_update_watcher_->InitWait("LOADED");
248 title_update_watcher_->Wait();
249 }
250 }
251
252 void TearDownInterstitialPage() {
253 // Close the interstitial.
254 interstitial_->DontProceed();
255 WaitForInterstitialDetach(shell()->web_contents());
256 interstitial_.reset();
257 }
258
259 bool FocusInputAndSelectText() {
260 return ExecuteScript(interstitial_->GetMainFrame(), "focus_select_input()");
261 }
262
263 bool GetInputText(std::string* input_text) {
264 return ExecuteScriptAndExtractString(interstitial_->GetMainFrame(),
265 "get_input_text()", input_text);
266 }
267
268 bool SetInputText(const std::string& text) {
269 return ExecuteScript(interstitial_->GetMainFrame(),
270 "set_input_text('" + text + "')");
271 }
272
273 base::string16 PerformCut() {
274 clipboard_message_watcher_->InitWait();
275 title_update_watcher_->InitWait("TEXT_CHANGED");
276 RenderFrameHostImpl* rfh =
277 static_cast<RenderFrameHostImpl*>(interstitial_->GetMainFrame());
278 rfh->GetRenderWidgetHost()->delegate()->Cut();
279 clipboard_message_watcher_->WaitForWriteCommit();
280 title_update_watcher_->Wait();
281 return clipboard_message_watcher_->last_text();
282 }
283
284 base::string16 PerformCopy() {
285 clipboard_message_watcher_->InitWait();
286 RenderFrameHostImpl* rfh =
287 static_cast<RenderFrameHostImpl*>(interstitial_->GetMainFrame());
288 rfh->GetRenderWidgetHost()->delegate()->Copy();
289 clipboard_message_watcher_->WaitForWriteCommit();
290 return clipboard_message_watcher_->last_text();
291 }
292
293 void PerformPaste() {
294 title_update_watcher_->InitWait("TEXT_CHANGED");
295 RenderFrameHostImpl* rfh =
296 static_cast<RenderFrameHostImpl*>(interstitial_->GetMainFrame());
297 rfh->GetRenderWidgetHost()->delegate()->Paste();
298 title_update_watcher_->Wait();
299 }
300
301 private:
302 void RunTaskOnIOThreadAndWait(const base::Closure& task) {
303 base::WaitableEvent completion(false, false);
304 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
305 base::Bind(&InterstitialPageImplTest::RunTask, this,
306 task, &completion));
307 completion.Wait();
308 }
309
310 void RunTask(const base::Closure& task, base::WaitableEvent* completion) {
311 task.Run();
312 completion->Signal();
313 }
314
315 scoped_ptr<InterstitialPageImpl> interstitial_;
316 ClipboardMessageWatcher* clipboard_message_watcher_;
317 InterstitialTitleUpdateWatcher* title_update_watcher_;
318
319 DISALLOW_COPY_AND_ASSIGN(InterstitialPageImplTest);
320 };
321
322 IN_PROC_BROWSER_TEST_F(InterstitialPageImplTest, Cut) {
323 SetUpInterstitialPage();
324
325 ASSERT_TRUE(SetInputText("text-to-cut"));
326 ASSERT_TRUE(FocusInputAndSelectText());
327
328 base::string16 clipboard_text = PerformCut();
dcheng 2015/07/02 07:06:25 I'd recommend having the helpers convert to std::s
mohsen 2015/07/02 16:41:01 Done.
329 EXPECT_EQ(base::ASCIIToUTF16("text-to-cut"), clipboard_text);
330
331 std::string input_text;
332 ASSERT_TRUE(GetInputText(&input_text));
333 EXPECT_EQ(std::string(), input_text);
334
335 TearDownInterstitialPage();
336 }
337
338 IN_PROC_BROWSER_TEST_F(InterstitialPageImplTest, Copy) {
339 SetUpInterstitialPage();
340
341 ASSERT_TRUE(SetInputText("text-to-copy"));
342 ASSERT_TRUE(FocusInputAndSelectText());
343
344 base::string16 clipboard_text = PerformCopy();
345 EXPECT_EQ(base::ASCIIToUTF16("text-to-copy"), clipboard_text);
346
347 std::string input_text;
348 ASSERT_TRUE(GetInputText(&input_text));
349 EXPECT_EQ("text-to-copy", input_text);
350
351 TearDownInterstitialPage();
352 }
353
354 IN_PROC_BROWSER_TEST_F(InterstitialPageImplTest, Paste) {
355 SetUpTestClipboard();
356 SetUpInterstitialPage();
357
358 SetClipboardText("text-to-paste");
359
360 ASSERT_TRUE(SetInputText(std::string()));
361 ASSERT_TRUE(FocusInputAndSelectText());
362
363 PerformPaste();
364
365 std::string input_text;
366 ASSERT_TRUE(GetInputText(&input_text));
367 EXPECT_EQ("text-to-paste", input_text);
368
369 TearDownInterstitialPage();
370 TearDownTestClipboard();
371 }
372
373 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698