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

Side by Side Diff: content/browser/tab_contents/render_view_host_manager_unittest.cc

Issue 10024066: TabContents -> WebContentsImpl, part 4. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: rebase Created 8 years, 8 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 | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2012 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 "base/utf_string_conversions.h"
6 #include "content/browser/browser_thread_impl.h"
7 #include "content/browser/mock_content_browser_client.h"
8 #include "content/browser/renderer_host/test_render_view_host.h"
9 #include "content/browser/site_instance_impl.h"
10 #include "content/browser/tab_contents/render_view_host_manager.h"
11 #include "content/browser/tab_contents/test_web_contents.h"
12 #include "content/browser/web_contents/navigation_entry_impl.h"
13 #include "content/browser/web_contents/navigation_controller_impl.h"
14 #include "content/common/test_url_constants.h"
15 #include "content/common/view_messages.h"
16 #include "content/public/browser/notification_details.h"
17 #include "content/public/browser/notification_source.h"
18 #include "content/public/browser/notification_types.h"
19 #include "content/public/browser/web_ui_controller.h"
20 #include "content/public/browser/web_ui_controller_factory.h"
21 #include "content/public/common/page_transition_types.h"
22 #include "content/public/common/url_constants.h"
23 #include "content/test/mock_render_process_host.h"
24 #include "content/test/test_browser_context.h"
25 #include "content/test/test_content_client.h"
26 #include "content/test/test_notification_tracker.h"
27 #include "googleurl/src/url_util.h"
28 #include "testing/gtest/include/gtest/gtest.h"
29 #include "ui/base/javascript_message_type.h"
30 #include "webkit/glue/webkit_glue.h"
31
32 using content::BrowserContext;
33 using content::BrowserThread;
34 using content::BrowserThreadImpl;
35 using content::MockRenderProcessHost;
36 using content::NavigationController;
37 using content::NavigationEntry;
38 using content::NavigationEntryImpl;
39 using content::RenderViewHost;
40 using content::RenderViewHostImpl;
41 using content::RenderViewHostImplTestHarness;
42 using content::SiteInstance;
43 using content::TestRenderViewHost;
44 using content::TestWebContents;
45 using content::WebContents;
46 using content::WebUI;
47 using content::WebUIController;
48
49 namespace {
50
51 class RenderViewHostManagerTestWebUIControllerFactory
52 : public content::WebUIControllerFactory {
53 public:
54 RenderViewHostManagerTestWebUIControllerFactory()
55 : should_create_webui_(false) {
56 }
57 virtual ~RenderViewHostManagerTestWebUIControllerFactory() {}
58
59 void set_should_create_webui(bool should_create_webui) {
60 should_create_webui_ = should_create_webui;
61 }
62
63 // WebUIFactory implementation.
64 virtual WebUIController* CreateWebUIControllerForURL(
65 WebUI* web_ui, const GURL& url) const OVERRIDE {
66 if (!(should_create_webui_ &&
67 content::GetContentClient()->HasWebUIScheme(url)))
68 return NULL;
69 return new WebUIController(web_ui);
70 }
71
72 virtual WebUI::TypeID GetWebUIType(BrowserContext* browser_context,
73 const GURL& url) const OVERRIDE {
74 return WebUI::kNoWebUI;
75 }
76
77 virtual bool UseWebUIForURL(BrowserContext* browser_context,
78 const GURL& url) const OVERRIDE {
79 return content::GetContentClient()->HasWebUIScheme(url);
80 }
81
82 virtual bool UseWebUIBindingsForURL(BrowserContext* browser_context,
83 const GURL& url) const OVERRIDE {
84 return content::GetContentClient()->HasWebUIScheme(url);
85 }
86
87 virtual bool IsURLAcceptableForWebUI(BrowserContext* browser_context,
88 const GURL& url) const OVERRIDE {
89 return false;
90 }
91
92 private:
93 bool should_create_webui_;
94
95 DISALLOW_COPY_AND_ASSIGN(RenderViewHostManagerTestWebUIControllerFactory);
96 };
97
98 class RenderViewHostManagerTestClient : public TestContentClient {
99 public:
100 RenderViewHostManagerTestClient() {
101 }
102
103 virtual bool HasWebUIScheme(const GURL& url) const OVERRIDE {
104 return url.SchemeIs(chrome::kChromeUIScheme);
105 }
106 };
107
108 class RenderViewHostManagerTestBrowserClient
109 : public content::MockContentBrowserClient {
110 public:
111 RenderViewHostManagerTestBrowserClient() {}
112 virtual ~RenderViewHostManagerTestBrowserClient() {}
113
114 void set_should_create_webui(bool should_create_webui) {
115 factory_.set_should_create_webui(should_create_webui);
116 }
117
118 // content::MockContentBrowserClient implementation.
119 virtual content::WebUIControllerFactory*
120 GetWebUIControllerFactory() OVERRIDE {
121 return &factory_;
122 }
123
124 private:
125 RenderViewHostManagerTestWebUIControllerFactory factory_;
126
127 DISALLOW_COPY_AND_ASSIGN(RenderViewHostManagerTestBrowserClient);
128 };
129
130 } // namespace
131
132 class RenderViewHostManagerTest
133 : public RenderViewHostImplTestHarness {
134 public:
135 virtual void SetUp() OVERRIDE {
136 RenderViewHostImplTestHarness::SetUp();
137 old_client_ = content::GetContentClient();
138 old_browser_client_ = content::GetContentClient()->browser();
139 content::SetContentClient(&client_);
140 content::GetContentClient()->set_browser(&browser_client_);
141 url_util::AddStandardScheme(chrome::kChromeUIScheme);
142 }
143
144 virtual void TearDown() OVERRIDE {
145 RenderViewHostImplTestHarness::TearDown();
146 content::GetContentClient()->set_browser(old_browser_client_);
147 content::SetContentClient(old_client_);
148 }
149
150 void set_should_create_webui(bool should_create_webui) {
151 browser_client_.set_should_create_webui(should_create_webui);
152 }
153
154 void NavigateActiveAndCommit(const GURL& url) {
155 // Note: we navigate the active RenderViewHost because previous navigations
156 // won't have committed yet, so NavigateAndCommit does the wrong thing
157 // for us.
158 controller().LoadURL(
159 url, content::Referrer(), content::PAGE_TRANSITION_LINK, std::string());
160 TestRenderViewHost* old_rvh = test_rvh();
161
162 // Simulate the ShouldClose_ACK that is received from the current renderer
163 // for a cross-site navigation.
164 if (old_rvh != active_rvh())
165 old_rvh->SendShouldCloseACK(true);
166
167 // Commit the navigation with a new page ID.
168 int32 max_page_id = contents()->GetMaxPageIDForSiteInstance(
169 active_rvh()->GetSiteInstance());
170 active_test_rvh()->SendNavigate(max_page_id + 1, url);
171
172 // Simulate the SwapOut_ACK that fires if you commit a cross-site navigation
173 // without making any network requests.
174 if (old_rvh != active_rvh())
175 old_rvh->OnSwapOutACK();
176 }
177
178 bool ShouldSwapProcesses(RenderViewHostManager* manager,
179 const NavigationEntryImpl* cur_entry,
180 const NavigationEntryImpl* new_entry) const {
181 return manager->ShouldSwapProcessesForNavigation(cur_entry, new_entry);
182 }
183
184 private:
185 RenderViewHostManagerTestClient client_;
186 RenderViewHostManagerTestBrowserClient browser_client_;
187 content::ContentClient* old_client_;
188 content::ContentBrowserClient* old_browser_client_;
189 };
190
191 // Tests that when you navigate from the New TabPage to another page, and
192 // then do that same thing in another tab, that the two resulting pages have
193 // different SiteInstances, BrowsingInstances, and RenderProcessHosts. This is
194 // a regression test for bug 9364.
195 TEST_F(RenderViewHostManagerTest, NewTabPageProcesses) {
196 BrowserThreadImpl ui_thread(BrowserThread::UI, MessageLoop::current());
197 const GURL kNtpUrl(chrome::kTestNewTabURL);
198 const GURL kDestUrl("http://www.google.com/");
199
200 // Navigate our first tab to the new tab page and then to the destination.
201 NavigateActiveAndCommit(kNtpUrl);
202 NavigateActiveAndCommit(kDestUrl);
203
204 // Make a second tab.
205 TestWebContents contents2(browser_context(), NULL);
206
207 // Load the two URLs in the second tab. Note that the first navigation creates
208 // a RVH that's not pending (since there is no cross-site transition), so
209 // we use the committed one.
210 contents2.GetController().LoadURL(
211 kNtpUrl, content::Referrer(), content::PAGE_TRANSITION_LINK,
212 std::string());
213 TestRenderViewHost* ntp_rvh2 = static_cast<TestRenderViewHost*>(
214 contents2.GetRenderManagerForTesting()->current_host());
215 EXPECT_FALSE(contents2.cross_navigation_pending());
216 ntp_rvh2->SendNavigate(100, kNtpUrl);
217
218 // The second one is the opposite, creating a cross-site transition and
219 // requiring a beforeunload ack.
220 contents2.GetController().LoadURL(
221 kDestUrl, content::Referrer(), content::PAGE_TRANSITION_LINK,
222 std::string());
223 EXPECT_TRUE(contents2.cross_navigation_pending());
224 TestRenderViewHost* dest_rvh2 = static_cast<TestRenderViewHost*>(
225 contents2.GetRenderManagerForTesting()->pending_render_view_host());
226 ASSERT_TRUE(dest_rvh2);
227 ntp_rvh2->SendShouldCloseACK(true);
228 dest_rvh2->SendNavigate(101, kDestUrl);
229 ntp_rvh2->OnSwapOutACK();
230
231 // The two RVH's should be different in every way.
232 EXPECT_NE(active_rvh()->GetProcess(), dest_rvh2->GetProcess());
233 EXPECT_NE(active_rvh()->GetSiteInstance(), dest_rvh2->GetSiteInstance());
234 EXPECT_NE(static_cast<SiteInstanceImpl*>(active_rvh()->GetSiteInstance())->
235 browsing_instance_,
236 static_cast<SiteInstanceImpl*>(dest_rvh2->GetSiteInstance())->
237 browsing_instance_);
238
239 // Navigate both to the new tab page, and verify that they share a
240 // SiteInstance.
241 NavigateActiveAndCommit(kNtpUrl);
242
243 contents2.GetController().LoadURL(
244 kNtpUrl, content::Referrer(), content::PAGE_TRANSITION_LINK,
245 std::string());
246 dest_rvh2->SendShouldCloseACK(true);
247 static_cast<TestRenderViewHost*>(contents2.GetRenderManagerForTesting()->
248 pending_render_view_host())->SendNavigate(102, kNtpUrl);
249 dest_rvh2->OnSwapOutACK();
250
251 EXPECT_EQ(active_rvh()->GetSiteInstance(),
252 contents2.GetRenderViewHost()->GetSiteInstance());
253 }
254
255 // Ensure that the browser ignores most IPC messages that arrive from a
256 // RenderViewHost that has been swapped out. We do not want to take
257 // action on requests from a non-active renderer. The main exception is
258 // for synchronous messages, which cannot be ignored without leaving the
259 // renderer in a stuck state. See http://crbug.com/93427.
260 TEST_F(RenderViewHostManagerTest, FilterMessagesWhileSwappedOut) {
261 BrowserThreadImpl ui_thread(BrowserThread::UI, MessageLoop::current());
262 const GURL kNtpUrl(chrome::kTestNewTabURL);
263 const GURL kDestUrl("http://www.google.com/");
264
265 // Navigate our first tab to the new tab page and then to the destination.
266 NavigateActiveAndCommit(kNtpUrl);
267 TestRenderViewHost* ntp_rvh = static_cast<TestRenderViewHost*>(
268 contents()->GetRenderManagerForTesting()->current_host());
269
270 // Send an update title message and make sure it works.
271 const string16 ntp_title = ASCIIToUTF16("NTP Title");
272 WebKit::WebTextDirection direction = WebKit::WebTextDirectionLeftToRight;
273 EXPECT_TRUE(ntp_rvh->OnMessageReceived(
274 ViewHostMsg_UpdateTitle(rvh()->GetRoutingID(), 0, ntp_title, direction)));
275 EXPECT_EQ(ntp_title, contents()->GetTitle());
276
277 // Navigate to a cross-site URL.
278 contents()->GetController().LoadURL(
279 kDestUrl, content::Referrer(), content::PAGE_TRANSITION_LINK,
280 std::string());
281 EXPECT_TRUE(contents()->cross_navigation_pending());
282 TestRenderViewHost* dest_rvh = static_cast<TestRenderViewHost*>(
283 contents()->GetRenderManagerForTesting()->pending_render_view_host());
284 ASSERT_TRUE(dest_rvh);
285 EXPECT_NE(ntp_rvh, dest_rvh);
286
287 // BeforeUnload finishes.
288 ntp_rvh->SendShouldCloseACK(true);
289
290 // Assume SwapOutACK times out, so the dest_rvh proceeds and commits.
291 dest_rvh->SendNavigate(101, kDestUrl);
292
293 // The new RVH should be able to update its title.
294 const string16 dest_title = ASCIIToUTF16("Google");
295 EXPECT_TRUE(dest_rvh->OnMessageReceived(
296 ViewHostMsg_UpdateTitle(rvh()->GetRoutingID(), 101, dest_title,
297 direction)));
298 EXPECT_EQ(dest_title, contents()->GetTitle());
299
300 // The old renderer, being slow, now updates the title. It should be filtered
301 // out and not take effect.
302 EXPECT_TRUE(ntp_rvh->is_swapped_out());
303 EXPECT_TRUE(ntp_rvh->OnMessageReceived(
304 ViewHostMsg_UpdateTitle(rvh()->GetRoutingID(), 0, ntp_title, direction)));
305 EXPECT_EQ(dest_title, contents()->GetTitle());
306
307 // We cannot filter out synchronous IPC messages, because the renderer would
308 // be left waiting for a reply. We pick RunBeforeUnloadConfirm as an example
309 // that can run easily within a unit test, and that needs to receive a reply
310 // without showing an actual dialog.
311 MockRenderProcessHost* ntp_process_host =
312 static_cast<MockRenderProcessHost*>(ntp_rvh->GetProcess());
313 ntp_process_host->sink().ClearMessages();
314 const string16 msg = ASCIIToUTF16("Message");
315 bool result = false;
316 string16 unused;
317 ViewHostMsg_RunBeforeUnloadConfirm before_unload_msg(
318 rvh()->GetRoutingID(), kNtpUrl, msg, false, &result, &unused);
319 // Enable pumping for check in BrowserMessageFilter::CheckCanDispatchOnUI.
320 before_unload_msg.EnableMessagePumping();
321 EXPECT_TRUE(ntp_rvh->OnMessageReceived(before_unload_msg));
322 EXPECT_TRUE(ntp_process_host->sink().GetUniqueMessageMatching(IPC_REPLY_ID));
323
324 // Also test RunJavaScriptMessage.
325 ntp_process_host->sink().ClearMessages();
326 ViewHostMsg_RunJavaScriptMessage js_msg(
327 rvh()->GetRoutingID(), msg, msg, kNtpUrl,
328 ui::JAVASCRIPT_MESSAGE_TYPE_CONFIRM, &result, &unused);
329 js_msg.EnableMessagePumping();
330 EXPECT_TRUE(ntp_rvh->OnMessageReceived(js_msg));
331 EXPECT_TRUE(ntp_process_host->sink().GetUniqueMessageMatching(IPC_REPLY_ID));
332 }
333
334 // When there is an error with the specified page, renderer exits view-source
335 // mode. See WebFrameImpl::DidFail(). We check by this test that
336 // EnableViewSourceMode message is sent on every navigation regardless
337 // RenderView is being newly created or reused.
338 TEST_F(RenderViewHostManagerTest, AlwaysSendEnableViewSourceMode) {
339 BrowserThreadImpl ui_thread(BrowserThread::UI, MessageLoop::current());
340 const GURL kNtpUrl(chrome::kTestNewTabURL);
341 const GURL kUrl("view-source:http://foo");
342
343 // We have to navigate to some page at first since without this, the first
344 // navigation will reuse the SiteInstance created by Init(), and the second
345 // one will create a new SiteInstance. Because current_instance and
346 // new_instance will be different, a new RenderViewHost will be created for
347 // the second navigation. We have to avoid this in order to exercise the
348 // target code patch.
349 NavigateActiveAndCommit(kNtpUrl);
350
351 // Navigate.
352 controller().LoadURL(
353 kUrl, content::Referrer(), content::PAGE_TRANSITION_TYPED, std::string());
354 // Simulate response from RenderView for FirePageBeforeUnload.
355 test_rvh()->OnMessageReceived(ViewHostMsg_ShouldClose_ACK(
356 rvh()->GetRoutingID(), true, base::TimeTicks(), base::TimeTicks()));
357 ASSERT_TRUE(pending_rvh()); // New pending RenderViewHost will be created.
358 RenderViewHost* last_rvh = pending_rvh();
359 int32 new_id = contents()->GetMaxPageIDForSiteInstance(
360 active_rvh()->GetSiteInstance()) + 1;
361 pending_test_rvh()->SendNavigate(new_id, kUrl);
362 EXPECT_EQ(controller().GetLastCommittedEntryIndex(), 1);
363 ASSERT_TRUE(controller().GetLastCommittedEntry());
364 EXPECT_TRUE(kUrl == controller().GetLastCommittedEntry()->GetURL());
365 EXPECT_FALSE(controller().GetPendingEntry());
366 // Because we're using TestWebContents and TestRenderViewHost in this
367 // unittest, no one calls TabContents::RenderViewCreated(). So, we see no
368 // EnableViewSourceMode message, here.
369
370 // Clear queued messages before load.
371 process()->sink().ClearMessages();
372 // Navigate, again.
373 controller().LoadURL(
374 kUrl, content::Referrer(), content::PAGE_TRANSITION_TYPED, std::string());
375 // The same RenderViewHost should be reused.
376 EXPECT_FALSE(pending_rvh());
377 EXPECT_TRUE(last_rvh == rvh());
378 test_rvh()->SendNavigate(new_id, kUrl); // The same page_id returned.
379 EXPECT_EQ(controller().GetLastCommittedEntryIndex(), 1);
380 EXPECT_FALSE(controller().GetPendingEntry());
381 // New message should be sent out to make sure to enter view-source mode.
382 EXPECT_TRUE(process()->sink().GetUniqueMessageMatching(
383 ViewMsg_EnableViewSourceMode::ID));
384 }
385
386 // Tests the Init function by checking the initial RenderViewHost.
387 TEST_F(RenderViewHostManagerTest, Init) {
388 // Using TestBrowserContext.
389 SiteInstanceImpl* instance =
390 static_cast<SiteInstanceImpl*>(SiteInstance::Create(browser_context()));
391 EXPECT_FALSE(instance->HasSite());
392
393 TestWebContents web_contents(browser_context(), instance);
394 RenderViewHostManager manager(&web_contents, &web_contents);
395
396 manager.Init(browser_context(), instance, MSG_ROUTING_NONE);
397
398 RenderViewHost* host = manager.current_host();
399 ASSERT_TRUE(host);
400 EXPECT_TRUE(instance == host->GetSiteInstance());
401 EXPECT_TRUE(&web_contents == host->GetDelegate());
402 EXPECT_TRUE(manager.GetRenderWidgetHostView());
403 EXPECT_FALSE(manager.pending_render_view_host());
404 }
405
406 // Tests the Navigate function. We navigate three sites consecutively and check
407 // how the pending/committed RenderViewHost are modified.
408 TEST_F(RenderViewHostManagerTest, Navigate) {
409 TestNotificationTracker notifications;
410
411 SiteInstance* instance = SiteInstance::Create(browser_context());
412
413 TestWebContents web_contents(browser_context(), instance);
414 notifications.ListenFor(
415 content::NOTIFICATION_RENDER_VIEW_HOST_CHANGED,
416 content::Source<NavigationController>(
417 &web_contents.GetController()));
418
419 // Create.
420 RenderViewHostManager manager(&web_contents, &web_contents);
421
422 manager.Init(browser_context(), instance, MSG_ROUTING_NONE);
423
424 RenderViewHost* host;
425
426 // 1) The first navigation. --------------------------
427 const GURL kUrl1("http://www.google.com/");
428 NavigationEntryImpl entry1(
429 NULL /* instance */, -1 /* page_id */, kUrl1, content::Referrer(),
430 string16() /* title */, content::PAGE_TRANSITION_TYPED,
431 false /* is_renderer_init */);
432 host = manager.Navigate(entry1);
433
434 // The RenderViewHost created in Init will be reused.
435 EXPECT_TRUE(host == manager.current_host());
436 EXPECT_FALSE(manager.pending_render_view_host());
437
438 // Commit.
439 manager.DidNavigateMainFrame(host);
440 // Commit to SiteInstance should be delayed until RenderView commit.
441 EXPECT_TRUE(host == manager.current_host());
442 ASSERT_TRUE(host);
443 EXPECT_FALSE(static_cast<SiteInstanceImpl*>(host->GetSiteInstance())->
444 HasSite());
445 static_cast<SiteInstanceImpl*>(host->GetSiteInstance())->SetSite(kUrl1);
446
447 // 2) Navigate to next site. -------------------------
448 const GURL kUrl2("http://www.google.com/foo");
449 NavigationEntryImpl entry2(
450 NULL /* instance */, -1 /* page_id */, kUrl2,
451 content::Referrer(kUrl1, WebKit::WebReferrerPolicyDefault),
452 string16() /* title */, content::PAGE_TRANSITION_LINK,
453 true /* is_renderer_init */);
454 host = manager.Navigate(entry2);
455
456 // The RenderViewHost created in Init will be reused.
457 EXPECT_TRUE(host == manager.current_host());
458 EXPECT_FALSE(manager.pending_render_view_host());
459
460 // Commit.
461 manager.DidNavigateMainFrame(host);
462 EXPECT_TRUE(host == manager.current_host());
463 ASSERT_TRUE(host);
464 EXPECT_TRUE(static_cast<SiteInstanceImpl*>(host->GetSiteInstance())->
465 HasSite());
466
467 // 3) Cross-site navigate to next site. --------------
468 const GURL kUrl3("http://webkit.org/");
469 NavigationEntryImpl entry3(
470 NULL /* instance */, -1 /* page_id */, kUrl3,
471 content::Referrer(kUrl2, WebKit::WebReferrerPolicyDefault),
472 string16() /* title */, content::PAGE_TRANSITION_LINK,
473 false /* is_renderer_init */);
474 host = manager.Navigate(entry3);
475
476 // A new RenderViewHost should be created.
477 EXPECT_TRUE(manager.pending_render_view_host());
478 ASSERT_EQ(host, manager.pending_render_view_host());
479
480 notifications.Reset();
481
482 // Commit.
483 manager.DidNavigateMainFrame(manager.pending_render_view_host());
484 EXPECT_TRUE(host == manager.current_host());
485 ASSERT_TRUE(host);
486 EXPECT_TRUE(static_cast<SiteInstanceImpl*>(host->GetSiteInstance())->
487 HasSite());
488 // Check the pending RenderViewHost has been committed.
489 EXPECT_FALSE(manager.pending_render_view_host());
490
491 // We should observe a notification.
492 EXPECT_TRUE(notifications.Check1AndReset(
493 content::NOTIFICATION_RENDER_VIEW_HOST_CHANGED));
494 }
495
496 // Tests the Navigate function. In this unit test we verify that the Navigate
497 // function can handle a new navigation event before the previous navigation
498 // has been committed. This is also a regression test for
499 // http://crbug.com/104600.
500 TEST_F(RenderViewHostManagerTest, NavigateWithEarlyReNavigation) {
501 TestNotificationTracker notifications;
502
503 SiteInstance* instance = SiteInstance::Create(browser_context());
504
505 TestWebContents web_contents(browser_context(), instance);
506 notifications.ListenFor(
507 content::NOTIFICATION_RENDER_VIEW_HOST_CHANGED,
508 content::Source<NavigationController>(
509 &web_contents.GetController()));
510
511 // Create.
512 RenderViewHostManager manager(&web_contents, &web_contents);
513
514 manager.Init(browser_context(), instance, MSG_ROUTING_NONE);
515
516 // 1) The first navigation. --------------------------
517 const GURL kUrl1("http://www.google.com/");
518 NavigationEntryImpl entry1(NULL /* instance */, -1 /* page_id */, kUrl1,
519 content::Referrer(), string16() /* title */,
520 content::PAGE_TRANSITION_TYPED,
521 false /* is_renderer_init */);
522 RenderViewHost* host = manager.Navigate(entry1);
523
524 // The RenderViewHost created in Init will be reused.
525 EXPECT_TRUE(host == manager.current_host());
526 EXPECT_FALSE(manager.pending_render_view_host());
527
528 // We should observe a notification.
529 EXPECT_TRUE(notifications.Check1AndReset(
530 content::NOTIFICATION_RENDER_VIEW_HOST_CHANGED));
531 notifications.Reset();
532
533 // Commit.
534 manager.DidNavigateMainFrame(host);
535
536 // Commit to SiteInstance should be delayed until RenderView commit.
537 EXPECT_TRUE(host == manager.current_host());
538 ASSERT_TRUE(host);
539 EXPECT_FALSE(static_cast<SiteInstanceImpl*>(host->GetSiteInstance())->
540 HasSite());
541 static_cast<SiteInstanceImpl*>(host->GetSiteInstance())->SetSite(kUrl1);
542
543 // 2) Cross-site navigate to next site. -------------------------
544 const GURL kUrl2("http://www.example.com");
545 NavigationEntryImpl entry2(
546 NULL /* instance */, -1 /* page_id */, kUrl2, content::Referrer(),
547 string16() /* title */, content::PAGE_TRANSITION_TYPED,
548 false /* is_renderer_init */);
549 RenderViewHostImpl* host2 = static_cast<RenderViewHostImpl*>(
550 manager.Navigate(entry2));
551 int host2_process_id = host2->GetProcess()->GetID();
552
553 // A new RenderViewHost should be created.
554 EXPECT_TRUE(manager.pending_render_view_host());
555 ASSERT_EQ(host2, manager.pending_render_view_host());
556 EXPECT_NE(host2, host);
557
558 // Check that the navigation is still suspended because the old RVH
559 // is not swapped out, yet.
560 EXPECT_TRUE(host2->are_navigations_suspended());
561 MockRenderProcessHost* test_process_host2 =
562 static_cast<MockRenderProcessHost*>(host2->GetProcess());
563 test_process_host2->sink().ClearMessages();
564 host2->NavigateToURL(kUrl2);
565 EXPECT_FALSE(test_process_host2->sink().GetUniqueMessageMatching(
566 ViewMsg_Navigate::ID));
567
568 // Allow closing the current Render View (precondition for swapping out
569 // the RVH): Simulate response from RenderView for ViewMsg_ShouldClose sent by
570 // FirePageBeforeUnload.
571 TestRenderViewHost* test_host = static_cast<TestRenderViewHost*>(host);
572 MockRenderProcessHost* test_process_host =
573 static_cast<MockRenderProcessHost*>(test_host->GetProcess());
574 EXPECT_TRUE(test_process_host->sink().GetUniqueMessageMatching(
575 ViewMsg_ShouldClose::ID));
576 test_host->SendShouldCloseACK(true);
577
578 // CrossSiteResourceHandler::StartCrossSiteTransition triggers a
579 // call of RenderViewHostManager::OnCrossSiteResponse before
580 // RenderViewHostManager::DidNavigateMainFrame is called.
581 // The RVH is not swapped out until the commit.
582 manager.OnCrossSiteResponse(host2->GetProcess()->GetID(),
583 host2->GetPendingRequestId());
584 EXPECT_TRUE(test_process_host->sink().GetUniqueMessageMatching(
585 ViewMsg_SwapOut::ID));
586 test_host->OnSwapOutACK();
587
588 EXPECT_EQ(host, manager.current_host());
589 EXPECT_FALSE(static_cast<RenderViewHostImpl*>(
590 manager.current_host())->is_swapped_out());
591 EXPECT_EQ(host2, manager.pending_render_view_host());
592 // There should be still no navigation messages being sent.
593 EXPECT_FALSE(test_process_host2->sink().GetUniqueMessageMatching(
594 ViewMsg_Navigate::ID));
595
596 // 3) Cross-site navigate to next site before 2) has committed. --------------
597 const GURL kUrl3("http://webkit.org/");
598 NavigationEntryImpl entry3(NULL /* instance */, -1 /* page_id */, kUrl3,
599 content::Referrer(), string16() /* title */,
600 content::PAGE_TRANSITION_TYPED,
601 false /* is_renderer_init */);
602 test_process_host->sink().ClearMessages();
603 RenderViewHost* host3 = manager.Navigate(entry3);
604
605 // A new RenderViewHost should be created. host2 is now deleted.
606 EXPECT_TRUE(manager.pending_render_view_host());
607 ASSERT_EQ(host3, manager.pending_render_view_host());
608 EXPECT_NE(host3, host);
609 EXPECT_NE(host3->GetProcess()->GetID(), host2_process_id);
610
611 // Navigations in the new RVH should be suspended, which is ok because the
612 // old RVH is not yet swapped out and can respond to a second beforeunload
613 // request.
614 EXPECT_TRUE(static_cast<RenderViewHostImpl*>(
615 host3)->are_navigations_suspended());
616 EXPECT_EQ(host, manager.current_host());
617 EXPECT_FALSE(static_cast<RenderViewHostImpl*>(
618 manager.current_host())->is_swapped_out());
619
620 // Simulate a response to the second beforeunload request.
621 EXPECT_TRUE(test_process_host->sink().GetUniqueMessageMatching(
622 ViewMsg_ShouldClose::ID));
623 test_host->SendShouldCloseACK(true);
624
625 // CrossSiteResourceHandler::StartCrossSiteTransition triggers a
626 // call of RenderViewHostManager::OnCrossSiteResponse before
627 // RenderViewHostManager::DidNavigateMainFrame is called.
628 // The RVH is not swapped out until the commit.
629 manager.OnCrossSiteResponse(host3->GetProcess()->GetID(),
630 static_cast<RenderViewHostImpl*>(
631 host3)->GetPendingRequestId());
632 EXPECT_TRUE(test_process_host->sink().GetUniqueMessageMatching(
633 ViewMsg_SwapOut::ID));
634 test_host->OnSwapOutACK();
635
636 // Commit.
637 manager.DidNavigateMainFrame(host3);
638 EXPECT_TRUE(host3 == manager.current_host());
639 ASSERT_TRUE(host3);
640 EXPECT_TRUE(static_cast<SiteInstanceImpl*>(host3->GetSiteInstance())->
641 HasSite());
642 // Check the pending RenderViewHost has been committed.
643 EXPECT_FALSE(manager.pending_render_view_host());
644
645 // We should observe a notification.
646 EXPECT_TRUE(notifications.Check1AndReset(
647 content::NOTIFICATION_RENDER_VIEW_HOST_CHANGED));
648 }
649
650 // Tests WebUI creation.
651 TEST_F(RenderViewHostManagerTest, WebUI) {
652 set_should_create_webui(true);
653 BrowserThreadImpl ui_thread(BrowserThread::UI, MessageLoop::current());
654 SiteInstance* instance = SiteInstance::Create(browser_context());
655
656 TestWebContents web_contents(browser_context(), instance);
657 RenderViewHostManager manager(&web_contents, &web_contents);
658
659 manager.Init(browser_context(), instance, MSG_ROUTING_NONE);
660
661 const GURL kUrl(chrome::kTestNewTabURL);
662 NavigationEntryImpl entry(NULL /* instance */, -1 /* page_id */, kUrl,
663 content::Referrer(), string16() /* title */,
664 content::PAGE_TRANSITION_TYPED,
665 false /* is_renderer_init */);
666 RenderViewHost* host = manager.Navigate(entry);
667
668 EXPECT_TRUE(host);
669 EXPECT_TRUE(host == manager.current_host());
670 EXPECT_FALSE(manager.pending_render_view_host());
671
672 // It's important that the site instance get set on the Web UI page as soon
673 // as the navigation starts, rather than lazily after it commits, so we don't
674 // try to re-use the SiteInstance/process for non DOM-UI things that may
675 // get loaded in between.
676 EXPECT_TRUE(static_cast<SiteInstanceImpl*>(host->GetSiteInstance())->
677 HasSite());
678 EXPECT_EQ(kUrl, host->GetSiteInstance()->GetSite());
679
680 // The Web UI is committed immediately because the RenderViewHost has not been
681 // used yet. UpdateRendererStateForNavigate() took the short cut path.
682 EXPECT_FALSE(manager.pending_web_ui());
683 EXPECT_TRUE(manager.web_ui());
684
685 // Commit.
686 manager.DidNavigateMainFrame(host);
687 }
688
689 // Tests that we don't end up in an inconsistent state if a page does a back and
690 // then reload. http://crbug.com/51680
691 TEST_F(RenderViewHostManagerTest, PageDoesBackAndReload) {
692 const GURL kUrl1("http://www.google.com/");
693 const GURL kUrl2("http://www.evil-site.com/");
694
695 // Navigate to a safe site, then an evil site.
696 // This will switch RenderViewHosts. We cannot assert that the first and
697 // second RVHs are different, though, because the first one may be promptly
698 // deleted.
699 contents()->NavigateAndCommit(kUrl1);
700 contents()->NavigateAndCommit(kUrl2);
701 RenderViewHost* evil_rvh = contents()->GetRenderViewHost();
702
703 // Now let's simulate the evil page calling history.back().
704 contents()->OnGoToEntryAtOffset(-1);
705 // We should have a new pending RVH.
706 // Note that in this case, the navigation has not committed, so evil_rvh will
707 // not be deleted yet.
708 EXPECT_NE(evil_rvh, contents()->GetRenderManagerForTesting()->
709 pending_render_view_host());
710
711 // Before that RVH has committed, the evil page reloads itself.
712 ViewHostMsg_FrameNavigate_Params params;
713 params.page_id = 1;
714 params.url = kUrl2;
715 params.transition = content::PAGE_TRANSITION_CLIENT_REDIRECT;
716 params.should_update_history = false;
717 params.gesture = NavigationGestureAuto;
718 params.was_within_same_page = false;
719 params.is_post = false;
720 params.content_state = webkit_glue::CreateHistoryStateForURL(GURL(kUrl2));
721 contents()->DidNavigate(evil_rvh, params);
722
723 // That should have cancelled the pending RVH, and the evil RVH should be the
724 // current one.
725 EXPECT_TRUE(contents()->GetRenderManagerForTesting()->
726 pending_render_view_host() == NULL);
727 EXPECT_EQ(evil_rvh, contents()->GetRenderManagerForTesting()->current_host());
728
729 // Also we should not have a pending navigation entry.
730 NavigationEntry* entry = contents()->GetController().GetActiveEntry();
731 ASSERT_TRUE(entry != NULL);
732 EXPECT_EQ(kUrl2, entry->GetURL());
733 }
734
735 // Ensure that we can go back and forward even if a SwapOut ACK isn't received.
736 // See http://crbug.com/93427.
737 TEST_F(RenderViewHostManagerTest, NavigateAfterMissingSwapOutACK) {
738 const GURL kUrl1("http://www.google.com/");
739 const GURL kUrl2("http://www.chromium.org/");
740
741 // Navigate to two pages.
742 contents()->NavigateAndCommit(kUrl1);
743 TestRenderViewHost* rvh1 = test_rvh();
744 contents()->NavigateAndCommit(kUrl2);
745 TestRenderViewHost* rvh2 = test_rvh();
746
747 // Now go back, but suppose the SwapOut_ACK isn't received. This shouldn't
748 // happen, but we have seen it when going back quickly across many entries
749 // (http://crbug.com/93427).
750 contents()->GetController().GoBack();
751 EXPECT_TRUE(rvh2->is_waiting_for_beforeunload_ack());
752 contents()->ProceedWithCrossSiteNavigation();
753 EXPECT_FALSE(rvh2->is_waiting_for_beforeunload_ack());
754 rvh2->SwapOut(1, 1);
755 EXPECT_TRUE(rvh2->is_waiting_for_unload_ack());
756
757 // The back navigation commits. We should proactively clear the
758 // is_waiting_for_unload_ack state to be safe.
759 const NavigationEntry* entry1 = contents()->GetController().GetPendingEntry();
760 rvh1->SendNavigate(entry1->GetPageID(), entry1->GetURL());
761 EXPECT_TRUE(rvh2->is_swapped_out());
762 EXPECT_FALSE(rvh2->is_waiting_for_unload_ack());
763
764 // We should be able to navigate forward.
765 contents()->GetController().GoForward();
766 contents()->ProceedWithCrossSiteNavigation();
767 const NavigationEntry* entry2 = contents()->GetController().GetPendingEntry();
768 rvh2->SendNavigate(entry2->GetPageID(), entry2->GetURL());
769 EXPECT_EQ(rvh2, rvh());
770 EXPECT_FALSE(rvh2->is_swapped_out());
771 EXPECT_TRUE(rvh1->is_swapped_out());
772 }
OLDNEW
« no previous file with comments | « content/browser/tab_contents/render_view_host_manager.cc ('k') | content/browser/tab_contents/tab_contents.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698